Making sense of Julia's do-blocks with functions

Viewed 90

Looking at the get function of base Julia, it is apparently intended to be used using a do-block:

  get(dict, key) do
      # default value calculated here
      time()
  end

The signature for get is get(Dictionary_name, Key_name, Default Value). This means that the do-block automatically inserts the default value as the last argument of get.

When I compare this syntax for the do-block with the one in this thread, I notice a difference:

my_function(f, container) = begin
    for element in container
        f(element)
    end
    return nothing
end

my_function([1,2,3]) do x  # equivlent to my_function(print, [1,2,3])
    print(x)
end

Notice how the function f is the very first argument of my_function, so the do-block inserts print as the first argument of my_function, not the last.

This is my confusion: Why is it that the do-block in one example inserts the variable as the last argument, but in another inserts in as the first?

2 Answers

get has several methods, you can check the docs to know what methods are, which you can list with methods(get). If you run methods(get, (Function, Dict, Any)) which would be the method called with the do block you show, you should see something like

# 1 method for generic function "get":
[1] get(default::Union{Function, Type}, h::Dict{K, V}, key) where {K, V} in Base at dict.jl:528

if instead you call methods(get, (Dict, Any, Any)) you get the method you were referring to.

If in the REPL you try to type

d = Dict("a"=>1, "b"=>2)
f(x) = time()
?get(f, d, "a")
# the '?' should enter the help?> mode but doesn't work if this is copy pasted

you should see the help message for the method of get that gets called in the case that you show with the do block which is equivalent to this one. So the do block is inserting the function as a first argument and this makes it call another method.

I hope this helps!

The called get function is not the one you thought.

In such cases, if you have any doubt, you can use the @which or @less macros :

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

julia> key="A"
"A"

julia> @which get(dict, key) do
           # default value calculated here
           time()
       end

which prints :

get(default::Union{Function, Type}, h::Dict{K, V}, key) where {K, V} in Base at dict.jl:528

The @which macro gives you the prototype of the called get function and where it is defined (here dict.jl line 528).

If you want to have a look at the source, use the @less macro:

julia> @less get(dict, key) do
           # default value calculated here
           time()
       end

which prints :

function get(default::Callable, h::Dict{K,V}, key) where V where K
    index = ht_keyindex(h, key)
    @inbounds return (index < 0) ? default() : h.vals[index]::V
end
Related