Change of behavior with `hasmethod(eval(Meta.parse()))`

One of my codes broke due to a change of behavior of hasmethod(eval(Meta.parse())) around version 1.10.4.

Prior to version 1.10.4 (e.g. 1.9.3)

julia> hasmethod(eval(Meta.parse("x->x")), (Real, ))
true

julia> f(s) = hasmethod(eval(Meta.parse(s)), (Real, )); f("x->x")
true

julia> f() = hasmethod(eval(Meta.parse("x->x")), (Real, )); f()
true

From version 1.10.4 on

julia> hasmethod(eval(Meta.parse("x->x")), (Real, ))
true

julia> f(s) = hasmethod(eval(Meta.parse(s)), (Real, )); f("x->x")
false

julia> f() = hasmethod(eval(Meta.parse("x->x")), (Real, )); f()
false

Could someone please explain the new behavior, that is somewhat counterintuitive?

Context: I want a function that takes a String as argument and, assuming that the String represents an anonymous function, checks that the anonymous function can be called with one Real argument. The goal is to then store this anonymous function and the String representing it in a structure. I read several posts telling that this is a bad idea and that there is almost certainly a better way to do it, but I could not find this better way.

You will need an invokelatest in there. Functions run in a fixed world-age, and eval will not respect that. This is (part of) why people tell you not to write code this way.

A ‘working’ version of your function would be

f(s) = invokelatest(hasmethod, eval(Meta.parse(s)), (Real,))
3 Likes

I’ll also point out that the ‘change in behaviour’ was actually a bugfix, since the answer you got on version 1.9 and below was actually incorrect. The function you created was not callable in that worldage

Here’s a demo:

julia> VERSION
v"1.9.4"

julia> function foo(s)
           f = eval(Meta.parse(s))
           @show hasmethod(f, (Real,))
           @show f(1)
       end;

julia> foo("x -> x")
hasmethod(f, (Real,)) = true
ERROR: MethodError: no method matching (::var"#7#8")(::Int64)
The applicable method may be too new: running in world age 33477, while current world is 33478.

whereas on v1.10 and up:

julia> VERSION
v"1.11.3"

julia> function foo(s)
           f = eval(Meta.parse(s))
           @show hasmethod(f, (Real,))
           @show f(1)
       end;

julia> foo("x -> x")
hasmethod(f, (Real,)) = false
ERROR: MethodError: no method matching (::var"#1#2")(::Int64)
The applicable method may be too new: running in world age 26684, while current world is 26685.
4 Likes

Thanks a lot! I had tried f(s) = hasmethod(invokelatest(eval(Meta.parse(s))), (Real, ))