Anonymous functions in Dictionary with generator syntax

I am running julia 1.0 on Manjaro Linux and have created a Dictionary like this:

d = Dict( i => ( s -> somefunction(s,i) ) for i in ["a", "b"])

where somefunction always returns a string.

Now I want to add another entry to d, so I tried this:

d["c"] = s -> "something else"

but I keep getting this error:

ERROR: MethodError: Cannot `convert` an object of type getfield(Main, Symbol("##84#85")) to an object of type getfield(Main, Symbol("##81#83")){String}
Closest candidates are:
  convert(::Type{T}, ::T) where T at essentials.jl:154
Stacktrace:
 [1] setindex!(::Dict{String,getfield(Main, Symbol("##81#83")){String}}, ::Function, ::String) at ./dict.jl:381
 [2] top-level scope at none:0

From my understanding it must have something to do with the way Julia assigns types to anonymous functions. I’ve experimented a bit and the following thing does work:

d = Dict{String,Any}( i => ( s -> somefunction(s,i) ) for i in ["a", "b"])
d["c"] = s -> "something else"

It also works flawlessly if I don’t use the generator syntax, but which is obviously not fit for larger arrays:

d = Dict( "a" => ( s -> somefunction(s,"a") ), "b" => ( s -> somefunction(s,"b") ))

Still, I can’t help but think there must be a better way, especially since I’d think that the Type Any for the Dictionary doesn’t leave much room for optimizations by the compiler. Is there a more elegant way to do this, maybe a type all of those abstract functions are a subtype of?

Thanks in advance!

The simplest thing is to use Function instead of Any. It won’t really help performance, but at least is a bit more specific about the intent.

If the dictionary is going to contain many totally different functions, then given code like d[x](s), the compiler doesn’t know what function will be called, precluding many optimizations. With the first comprehension, we know all the functions are “similar enough” that they share code, so some optimizations are possible.

Thanks for the fast response!
Makes sense, seems like the Function-Type is also what’s assigned in the example without the generator syntax. Just wondering why the compiler can’t do the same with this syntax.

Functions created by different expressions will have different types. For s->f(s,"a") and s->f(s,"b") to somehow share code, we would have to analyze them and prove that they are similar enough to share code. So far, that is too much to ask of the compiler. You can work around that by arranging for the functions to come from the same -> expression:

get_function(str) = s->somefunction(s, str)

get_function("a"), get_function("b")
2 Likes