Trying to understand how lazy computation works

Trying to understand the following argument

A zero-argument anonymous function is written as ()->3. The idea of a function with no arguments may seem strange, but is useful for “delaying” a computation. In this usage, a block of code is wrapped in a zero-argument function, which is later invoked by calling it as f.

get(()->time(), dict, key)

from here

I used the following expressions using two different methods of the get!() function:

julia> d = Dict()
Dict{Any, Any}()

julia> get!(()->"d(b)", d,'b')

julia> d
Dict{Any, Any} with 1 entry:
  'b' => "d(b)"

julia> get!(d,'a',()->"d(a)")
#65 (generic function with 1 method)

julia> d
Dict{Any, Any} with 2 entries:
  'a' => #65
  'b' => "d(b)"

why in the second case the anonymous function is not evaluated?
Another question: what does the symbol "#65 " stand for?
I understand it’s referring to the anonymous function somehow, but…
Is it possible (although perhaps not recommended) to use it in any way?

i tried giving it as an argument to typeof() and methods() but my REPL stops as waiting to complete the expression (!?)

  1. If you look at the function semantics in the manual, you’ll see that this behavior is what’s intended – in one case, you store the anonymous function in the dictionary and in another you store the result of evaluating the anonymous function in the dictionary.
  2. #65 is a kind of internal name for the anonymous function. I am not aware of it being possible to use and would think it’s not wise to use given that the point of anonymous functions is that they don’t have canonical names.

This is because the name is a comment, no?

yes :smiley:.

also like this methods(var"#15()") and typeof(var"#15()") doesn’t give any result

julia> get!(d,'d',()->"z-arg")()

julia> d
Dict{Any, Any} with 4 entries:
  'a' => sqrt
  'd' => #9
  'b' => "d(b)"

julia> d['a'](9)

julia> d['d']()

# does this mean that `#nnn` is not just a comment but a variable referring to the anonymous function?

Note that #15 is the function name, in the same sense that #sin is the function name of sin:

julia> typeof(sin)


The difference is that the function named #sin is bound to a const global named sin which we can use to call the function. The anonymous function #15 however is not bound to a global variable so (as far as I can see) it cannot be called by its name.

In particular, #15 is not bound to a variable of that name! That’s why var"#15" returns an error. But you can actually make the binding yourself:

var"#15" = d['a']   # assuming that d['a'] is the anonymous function #15

But we could also bind an unrelated value to var"#15", or bind #15 to var"#19" or whatever…

There’s also a name for the type of the anonymous function, and this one is actually bound to a variable, you can access it with var"#15#16. You can use that in a method declaration to dispatch on the type of the anonymous function, or with var"#15#16" you can get the method table, but I don’t know how to use it to call the function itself.



julia> get!(d,'g',()->"g-arg") 
#23 (generic function with 1 method)

julia> d
Dict{Any, Any} with 4 entries:
  'g' => #23
  'd' => #21
  'a' => sqrt
  'e' => "eheheh"

julia> var"#23#24".instance()
1 Like

Indeed that works! However I think this field is only defined for singleton types, so it won’t work if the anonymous function is a closure:

c = let a=1

julia> typeof(c)

julia> var"#15#16"{Int64}.instance
ERROR: UndefRefError: access to undefined reference