Multiple function definitions inside let block override previous definitions

I haven’t stumbled across anyone else reporting it, so here’s what I’ve encountered:

let isintvec(v) = false,
    isintvec(v::Vector{U}) where {U<:Integer} = true
    isintvec(3)
end

throws MethodError

thank god I write functions from the least specific to the most or else I’d have a nasty bug :slight_smile:

1 Like

let headers are intended for simple variable assignments, and although the assignment form of method definition seems to be accepted by the parser, what it actually seems to do is rearrange it to assignment of anonymous functions to a variable, hence the second assignment overriding the first. You must write them in the let body to recognize the name isintvec as a multimethod’s, they’ll get lowered as callable objects to allow functions to capture variables aka closures.

julia> let isintvec(v) = false, # define in header
           isintvec(v::Vector{U}) where {U<:Integer} = true
           methods(isintvec)
       end
# 1 method for anonymous function "#9#isintvec":
[1] (::var"#9#isintvec#30")(v::Vector{U}) where U<:Integer in Main at REPL[24]:2

julia> let isintvec = v -> false, # what really happens
           isintvec = ((v::Vector{U}) where {U<:Integer}) -> true
           methods(isintvec)
       end
# 1 method for anonymous function "#36":
[1] (::var"#36#38")(v::Vector{U}) where U<:Integer in Main at REPL[34]:2

julia> let 
         # define function inside let body, like inside function body
         isintvec(v) = false
         isintvec(v::Vector{U}) where {U<:Integer} = true
         methods(isintvec)
       end
# 2 methods for callable object:
[1] (::var"#isintvec#31")(v::Vector{U}) where U<:Integer in Main at REPL[25]:3
[2] (::var"#isintvec#31")(v) in Main at REPL[25]:2

Even when the variable name is not recognized as a multimethod name, you can still use it to add methods to anonymous functions. In this example you could write (::typeof(isintvec))(v) = false.

The function block form of method definition fails to make a let-local variable, by the way. Although, in the right side of a let-variable assignment that accesses the outer scope, you can define a method that belongs to the outer scope:

julia> let function foo(x) true end
         foo(1)
       end
ERROR: syntax: invalid let syntax around REPL[30]:1

julia> let bar = function foo(x) true end # right side in global scope
         bar(1)
       end
true

julia> foo
foo (generic function with 1 method)

julia> let
         let bar = function foo2(x) true end # right side in outer scope
           bar(1)
         end
         methods(foo2)
       end
# 1 method for callable object:
[1] (::var"#foo2#65")(x) in Main at REPL[50]:2
1 Like