Type inference for a singleton of type Function

I have an object that stores (amongst other things) a vector of functions. When I use multiple functions, everything works nicely. However, if I pass it a vector with a single function, I’m getting type-inference problems.

Here’s a simplified example:

function times2(i)
    2i
end

function times_n(i,n)
    i*n
end

This works:

[times2, i -> times_n(i,8)]

And gives

2-element Array{Function,1}:
 times2
 var"#136#137"()

Which is what I want (would’ve preferred a more informative name but that’s just a matter of convenience).
However, instead if I try:

l = [times2]
push!(l, i -> times_n(i,8))

I get an error:

MethodError: Cannot `convert` an object of type var"#168#169" to an object of type typeof(times2)
Closest candidates are:
  convert(::Type{T}, !Matched::T) where T at essentials.jl:171

Stacktrace:
 [1] push!(::Array{typeof(times2),1}, ::Function) at ./array.jl:913
 [2] top-level scope at In[152]:2

This also happens if I initialise l with the anonymous function and then try to add times2.

I’ve tried annotating the relevant expressions with ::Function but I still get that l is a list of objects of type typeof(times2).

I apologise if this is a trivial question - I’ve tried to search for an answer on the forum but couldn’t find any. Any help would be much appreciated!

I don’t know why your example doesn’t work, but maybe this is an alternative to your code:

l = [times2]
l = vcat(l,[i -> times_n(i,8)])

The reason you cannot push to the [times2] vector is this:

julia> typeof([times2])
Array{typeof(times2),1}

The array created with [times2] is specifically an array that is allowed to contain objects of type typeof(times2). This is in much the same way that an array of Ints may only store Ints and initializing [15] will create such a vector (you cannot then push 1.6 to it).

This works just fine:

julia> v = [times2]
1-element Array{typeof(times2),1}:
 times2 (generic function with 1 method)

julia> push!(v, times2)
2-element Array{typeof(times2),1}:
 times2 (generic function with 1 method)
 times2 (generic function with 1 method)

If you want a vector that can store generic Functions, you can declare it like so:

julia> v = Function[times2]
1-element Array{Function,1}:
 times2 (generic function with 1 method)

julia> push!(v, times_n)
2-element Array{Function,1}:
 times2 (generic function with 1 method)
 times_n (generic function with 1 method)
2 Likes

Did you try

l = Function[times2]

?
Edit:
One min too late. What @tomerarnon said!

2 Likes

Thanks, this was exactly what I needed!

Just out of curiosity - if I’ve just defined times2 as a function, why can’t Julia infer that its type is Function? And why doesn’t type annotation help?

I think that has to do with multiple dispatch. When you create a function a struct with that function is created and the different methods are then instances of this struct.
So

times2(i::Int)
times2(f::Float64)

Are both of type times2

A more deep reasoning can be found here:

3 Likes

It does, but Function is an abstract type, and julia will always prefer a concrete type when possible

julia> typeof(times2)
typeof(times2)

julia> supertype(typeof(times2))
Function

Another way to look at this:

julia> times2 isa Function
true

julia> isconcretetype(Function)
false

julia> length(subtypes(Function)) # how many things are there that are <: Function
13829
4 Likes