Question about use of generated functions


#1

In a package of mines I need to often create an empty Dict to which I later add elements. In order to avoid always allocating a new empty Dict, I thought about exploiting generated functions in this way:

@generated foo(x) = Dict{x,x}()

So now foo(0.0) is very fast an cheaper than always running Dict{Float64,Float64}(). Is this considered a good use of generated functions? Or is it an abuse and it’s prone to fail at some point?

Edit: contrary to what I wrote in the first version of the message, I’m more interested in Base.ImmutableDict rather than Dict.


#2

No, this way you create a single global Dict{Float64,Float64}() that all share the same data:

julia> @generated foo(x) = Dict{x,x}()
foo (generic function with 1 method)

julia> d1 = foo(0.0)
Dict{Float64,Float64} with 0 entries

julia> d2 = foo(0.0)
Dict{Float64,Float64} with 0 entries

julia> d1[1.0] = 2.0
2.0

julia> d2
Dict{Float64,Float64} with 1 entry:
  1.0 => 2.0

julia> d1 === d2
true

If you want multiple instances of a Dict, you have to create them at some point, and no language feature can change it.


#3

We don’t guarantee that the generator is only run once. So yes the behavior can change.


#4

Sorry for the simplification, but actually I don’t use a Dict but a type very similar to Base.ImmutableDict which is immutable.


#5

Base.ImmutableDict doesn’t have the same problem as Dict (sorry, when writing the first message I didn’t consider I was simplifying too much):

julia> @generated foo(x) = Base.ImmutableDict{x,x}()
foo (generic function with 1 method)

julia> d1 = foo(0.0)
Base.ImmutableDict{Float64,Float64} with 0 entries

julia> d2 = foo(0.0)
Base.ImmutableDict{Float64,Float64} with 0 entries

julia> d1 = Base.ImmutableDict(d1, 3=>4)
Base.ImmutableDict{Float64,Float64} with 1 entry:
  3.0 => 4.0

julia> d2
Base.ImmutableDict{Float64,Float64} with 0 entries

The question still holds: is this use of @generated fine? I don’t care the body of the function isn’t run exactly once, the important is that it isn’t always.


#6

Embedding an object in the returned AST (or returning an object) is fine. Do note that the construction of the object cannot depend on any methods defined later, otherwise it’s fine.


#7

If I understand you correctly, you can also just use a constant for it.


#8

Thank you so much. I don’t redefine the constructor method, so should be good.


#9

I don’t know the type of dictionary’s elements, for my use case it can be any <:AbstractFloat, that’s why I need a function.


#10

Just to clarify, I mean that if you need to have, say, 5 distinct (mutable) objects, you have to create 5 distinct objects. It’s technically impossible to allocate memory only for one of them, but fill it with 5 different data, regardless of the features available in the language.