Macro expansion and evaluation when the module with the macro definition is not in scope

Let’s say I’m developing a macro @mymacro in a package called CoolStuff. Suppose that this

@mymacro 42

expands to this:

CoolStuff.foo(42)

Now suppose that the user loads @mymacro by doing using CoolStuff: @mymacro instead of using CoolStuff. Intuitively I would think that @mymacro 42 would fail because the expanded code refers to the CoolStuff module, but the CoolStuff module is not actually in scope. However, when I tested it out on an existing package, it seems to work. Here’s an example with the Memoization.jl package:

julia> using Memoization: @memoize

julia> Memoization
ERROR: UndefVarError: `Memoization` not defined

julia> Memoization.get_cache
ERROR: UndefVarError: `Memoization` not defined
Stacktrace:
 [1] top-level scope
   @ REPL[3]:1

julia> @memoize f(x) = (println("Computed $x"); x)
f (generic function with 1 method)

julia> f(2)
Computed 2
2

julia> f(2)
2

So, in this example the @memoize function works fine, even though the Memoization module is not in scope. If we look at the expanded code for @memoize, we can see that the expanded code refers to Memoization and Memoization.get_cache (and various other functions internal to Memoization.jl):

Expanded `@memoize`
quote
    #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:155 =#
    var"#19#func" = begin
            $(Expr(:meta, :doc))
            function f(x::Any; )
                #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:206 =#
                var"##cache#231" = (Memoization.get_cache)(IdDict, f)
                #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:207 =#
                var"##getter#230"() = begin
                        #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:207 =#
                        begin
                            #= REPL[6]:1 =#
                            println("Computed $(x)")
                            #= REPL[6]:1 =#
                            x
                        end
                    end
                #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:208 =#
                var"##T#229" = (Core.Compiler.return_type)(var"##getter#230", (Tuple){})
                #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:209 =#
                (Memoization._get!)(var"##getter#230", var"##cache#231", ((x,), ()))::var"##T#229"
            end
        end
    #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:156 =#
    begin
        #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:158 =#
        local var"#20#cache_constructor_expr′" = Memoization._get!((()->begin
                            #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:158 =#
                            $(QuoteNode(IdDict))
                        end), Memoization.cache_constructor_exprs, var"#19#func")
        #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:159 =#
        if var"#20#cache_constructor_expr′" != $(QuoteNode(IdDict))
            #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:160 =#
            Memoization.error("$(var"#19#func") is already memoized with $(var"#20#cache_constructor_expr′")")
        end
    end
    #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:163 =#
    if Memoization.statically_memoizable(Memoization.typeof(var"#19#func"))
        #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:168 =#
        if Memoization.which((Memoization)._static_get_cache, Memoization.Tuple{Memoization.typeof(var"#19#func")}) == Memoization.which((Memoization)._static_get_cache, Memoization.Tuple{Memoization.Any})
            #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:172 =#
            Core.eval(Main, Core._expr(:macrocall, Symbol("@generated"), $(QuoteNode(:(#= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:172 =#))), Core._expr(:function, Core._expr(:call, $(Expr(:copyast, :($(QuoteNode(:((Memoization)._static_get_cache)))))), Core._expr(:(::), Core._expr(:call, :typeof, var"#19#func"))), $(Expr(:copyast, :($(QuoteNode(quote
    #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:172 =#
    #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:173 =#
    (IdDict)()
end))))))))
        end
        #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:180 =#
        (Memoization.empty_cache!)(f)
    end
    #= /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl:182 =#
    var"#19#func"
end

So, how does that work? Does evaluation of macro calls have some extra magic that lets Julia know where to find Memoization.get_cache even though Memoization is not in scope?

1 Like

It looks like there is some extra magic that you don’t see in the printout from @macroexpand. If you use dump on the expression returned by @macroexpand you can see more detail. So, if you do this:

ex = @macroexpand @memoize f(x) = (println("Computed $x"); x)
dump(ex; maxdepth=1000)

then you will see some things like this:

6: Expr
      head: Symbol if
      args: Array{Any}((2,))
        1: Expr
          head: Symbol call
          args: Array{Any}((2,))
            1: GlobalRef
              mod: Module Memoization
              name: Symbol statically_memoizable
              binding: Core.Binding
                value: statically_memoizable (function of type typeof(Memoization.statically_memoizable))
                globalref: GlobalRef#= circular reference @-2 =#
                owner: Core.Binding#= circular reference @-1 =#
                ty: Any
                flags: UInt8 0x01

So, that GlobalRef seems to be part of the magic.

There are also some entries like this:

4: Expr
  head: Symbol call
  args: Array{Any}((2,))
    1: empty_cache! (function of type typeof(Memoization.empty_cache!))
    2: Symbol f

Note that arg 1 has a function value, empty_cache!, rather than a Symbol empty_cache!. I didn’t realize that was allowed, but the manual says “Expressions provided by the parser generally only have symbols, other expressions, and literal values as their args, whereas expressions constructed by Julia code can have arbitrary run-time values without literal forms as args.” That might be a new addition to the manual, because I don’t remember seeing that before. That also really messes with my mental model for how macros work. I thought macros were just syntax manipulation…

Here’s the full dump of the expression returned by @macroexpand:

Full dump of macroexpand
Expr
  head: Symbol block
  args: Array{Any}((8,))
    1: LineNumberNode
      line: Int64 155
      file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
    2: Expr
      head: Symbol =
      args: Array{Any}((2,))
        1: Symbol #21#func
        2: Expr
          head: Symbol block
          args: Array{Any}((2,))
            1: Expr
              head: Symbol meta
              args: Array{Any}((1,))
                1: Symbol doc
            2: Expr
              head: Symbol function
              args: Array{Any}((2,))
                1: Expr
                  head: Symbol call
                  args: Array{Any}((3,))
                    1: Symbol f
                    2: Expr
                      head: Symbol parameters
                      args: Array{Any}((0,))
                    3: Expr
                      head: Symbol ::
                      args: Array{Any}((2,))
                        1: Symbol x
                        2: Symbol Any
                2: Expr
                  head: Symbol block
                  args: Array{Any}((8,))
                    1: LineNumberNode
                      line: Int64 206
                      file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
                    2: Expr
                      head: Symbol =
                      args: Array{Any}((2,))
                        1: Symbol ##cache#234
                        2: Expr
                          head: Symbol call
                          args: Array{Any}((3,))
                            1: get_cache (function of type typeof(Memoization.get_cache))
                            2: UnionAll
                              var: TypeVar
                                name: Symbol K
                                lb: Union{}
                                ub: Any
                              body: UnionAll
                                var: TypeVar
                                  name: Symbol V
                                  lb: Union{}
                                  ub: Any
                                body: IdDict{K, V} <: AbstractDict{K, V}
                                  ht::Vector{Any}
                                  count::Int64
                                  ndel::Int64
                            3: Symbol f
                    3: LineNumberNode
                      line: Int64 207
                      file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
                    4: Expr
                      head: Symbol =
                      args: Array{Any}((2,))
                        1: Expr
                          head: Symbol call
                          args: Array{Any}((1,))
                            1: Symbol ##getter#233
                        2: Expr
                          head: Symbol block
                          args: Array{Any}((2,))
                            1: LineNumberNode
                              line: Int64 207
                              file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
                            2: Expr
                              head: Symbol block
                              args: Array{Any}((4,))
                                1: LineNumberNode
                                  line: Int64 1
                                  file: Symbol REPL[7]
                                2: Expr
                                  head: Symbol call
                                  args: Array{Any}((2,))
                                    1: Symbol println
                                    2: Expr
                                      head: Symbol string
                                      args: Array{Any}((2,))
                                        1: String "Computed "
                                        2: Symbol x
                                3: LineNumberNode
                                  line: Int64 1
                                  file: Symbol REPL[7]
                                4: Symbol x
                    5: LineNumberNode
                      line: Int64 208
                      file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
                    6: Expr
                      head: Symbol =
                      args: Array{Any}((2,))
                        1: Symbol ##T#232
                        2: Expr
                          head: Symbol call
                          args: Array{Any}((3,))
                            1: return_type (function of type typeof(Core.Compiler.return_type))
                            2: Symbol ##getter#233
                            3: Expr
                              head: Symbol curly
                              args: Array{Any}((1,))
                                1: Tuple <: Any
                    7: LineNumberNode
                      line: Int64 209
                      file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
                    8: Expr
                      head: Symbol ::
                      args: Array{Any}((2,))
                        1: Expr
                          head: Symbol call
                          args: Array{Any}((4,))
                            1: _get! (function of type typeof(Memoization._get!))
                            2: Symbol ##getter#233
                            3: Symbol ##cache#234
                            4: Expr
                              head: Symbol tuple
                              args: Array{Any}((2,))
                                1: Expr
                                  head: Symbol tuple
                                  args: Array{Any}((1,))
                                    1: Symbol x
                                2: Expr
                                  head: Symbol tuple
                                  args: Array{Any}((0,))
                        2: Symbol ##T#232
    3: LineNumberNode
      line: Int64 156
      file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
    4: Expr
      head: Symbol block
      args: Array{Any}((4,))
        1: LineNumberNode
          line: Int64 158
          file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
        2: Expr
          head: Symbol local
          args: Array{Any}((1,))
            1: Expr
              head: Symbol =
              args: Array{Any}((2,))
                1: Symbol #22#cache_constructor_expr′
                2: Expr
                  head: Symbol call
                  args: Array{Any}((4,))
                    1: GlobalRef
                      mod: Module Memoization
                      name: Symbol _get!
                      binding: Core.Binding
                        value: _get! (function of type typeof(Memoization._get!))
                        globalref: GlobalRef#= circular reference @-2 =#
                        owner: Core.Binding#= circular reference @-1 =#
                        ty: Any
                        flags: UInt8 0x01
                    2: Expr
                      head: Symbol ->
                      args: Array{Any}((2,))
                        1: Expr
                          head: Symbol tuple
                          args: Array{Any}((0,))
                        2: Expr
                          head: Symbol block
                          args: Array{Any}((2,))
                            1: LineNumberNode
                              line: Int64 158
                              file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
                            2: QuoteNode
                              value: UnionAll
                                var: TypeVar
                                  name: Symbol K
                                  lb: Union{}
                                  ub: Any
                                body: UnionAll
                                  var: TypeVar
                                    name: Symbol V
                                    lb: Union{}
                                    ub: Any
                                  body: IdDict{K, V} <: AbstractDict{K, V}
                                    ht::Vector{Any}
                                    count::Int64
                                    ndel::Int64
                    3: GlobalRef
                      mod: Module Memoization
                      name: Symbol cache_constructor_exprs
                      binding: Core.Binding
                        value: IdDict{Any, Any}
                          ht: Array{Any}((32,))
                            1: #undef
                            2: #undef
                            3: #undef
                            4: #undef
                            5: #undef
                            ...
                            28: #undef
                            29: #undef
                            30: #undef
                            31: #undef
                            32: #undef
                          count: Int64 1
                          ndel: Int64 0
                        globalref: GlobalRef#= circular reference @-2 =#
                        owner: Core.Binding#= circular reference @-1 =#
                        ty: Any
                        flags: UInt8 0x01
                    4: Symbol #21#func
        3: LineNumberNode
          line: Int64 159
          file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
        4: Expr
          head: Symbol if
          args: Array{Any}((2,))
            1: Expr
              head: Symbol call
              args: Array{Any}((3,))
                1: GlobalRef
                  mod: Module Memoization
                  name: Symbol !=
                  binding: Core.Binding
                    value: #undef
                    globalref: GlobalRef#= circular reference @-2 =#
                    owner: Core.Binding
                      value: != (function of type typeof(!=))
                      globalref: GlobalRef
                        mod: Module Base
                        name: Symbol !=
                        binding: Core.Binding#= circular reference @-2 =#
                      owner: Core.Binding#= circular reference @-1 =#
                      ty: #undef
                      flags: UInt8 0x03
                    ty: #undef
                    flags: UInt8 0x00
                2: Symbol #22#cache_constructor_expr′
                3: QuoteNode
                  value: UnionAll
                    var: TypeVar
                      name: Symbol K
                      lb: Union{}
                      ub: Any
                    body: UnionAll
                      var: TypeVar
                        name: Symbol V
                        lb: Union{}
                        ub: Any
                      body: IdDict{K, V} <: AbstractDict{K, V}
                        ht::Vector{Any}
                        count::Int64
                        ndel::Int64
            2: Expr
              head: Symbol block
              args: Array{Any}((2,))
                1: LineNumberNode
                  line: Int64 160
                  file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
                2: Expr
                  head: Symbol call
                  args: Array{Any}((2,))
                    1: GlobalRef
                      mod: Module Memoization
                      name: Symbol error
                      binding: Core.Binding
                        value: #undef
                        globalref: GlobalRef#= circular reference @-2 =#
                        owner: #undef
                        ty: #undef
                        flags: UInt8 0x00
                    2: Expr
                      head: Symbol string
                      args: Array{Any}((3,))
                        1: Symbol #21#func
                        2: String " is already memoized with "
                        3: Symbol #22#cache_constructor_expr′
    5: LineNumberNode
      line: Int64 163
      file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
    6: Expr
      head: Symbol if
      args: Array{Any}((2,))
        1: Expr
          head: Symbol call
          args: Array{Any}((2,))
            1: GlobalRef
              mod: Module Memoization
              name: Symbol statically_memoizable
              binding: Core.Binding
                value: statically_memoizable (function of type typeof(Memoization.statically_memoizable))
                globalref: GlobalRef#= circular reference @-2 =#
                owner: Core.Binding#= circular reference @-1 =#
                ty: Any
                flags: UInt8 0x01
            2: Expr
              head: Symbol call
              args: Array{Any}((2,))
                1: GlobalRef
                  mod: Module Memoization
                  name: Symbol typeof
                  binding: Core.Binding
                    value: #undef
                    globalref: GlobalRef#= circular reference @-2 =#
                    owner: Core.Binding
                      value: typeof (function of type typeof(typeof))
                      globalref: GlobalRef
                        mod: Module Core
                        name: Symbol typeof
                        binding: Core.Binding#= circular reference @-2 =#
                      owner: Core.Binding#= circular reference @-1 =#
                      ty: Any
                      flags: UInt8 0x03
                    ty: #undef
                    flags: UInt8 0x00
                2: Symbol #21#func
        2: Expr
          head: Symbol block
          args: Array{Any}((4,))
            1: LineNumberNode
              line: Int64 168
              file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
            2: Expr
              head: Symbol if
              args: Array{Any}((2,))
                1: Expr
                  head: Symbol call
                  args: Array{Any}((3,))
                    1: GlobalRef
                      mod: Module Memoization
                      name: Symbol ==
                      binding: Core.Binding
                        value: #undef
                        globalref: GlobalRef#= circular reference @-2 =#
                        owner: Core.Binding
                          value: == (function of type typeof(==))
                          globalref: GlobalRef
                            mod: Module Base
                            name: Symbol ==
                            binding: Core.Binding#= circular reference @-2 =#
                          owner: Core.Binding#= circular reference @-1 =#
                          ty: #undef
                          flags: UInt8 0x03
                        ty: #undef
                        flags: UInt8 0x00
                    2: Expr
                      head: Symbol call
                      args: Array{Any}((3,))
                        1: GlobalRef
                          mod: Module Memoization
                          name: Symbol which
                          binding: Core.Binding
                            value: #undef
                            globalref: GlobalRef#= circular reference @-2 =#
                            owner: Core.Binding
                              value: which (function of type typeof(which))
                              globalref: GlobalRef
                                mod: Module Base
                                name: Symbol which
                                binding: Core.Binding#= circular reference @-2 =#
                              owner: Core.Binding#= circular reference @-1 =#
                              ty: #undef
                              flags: UInt8 0x03
                            ty: #undef
                            flags: UInt8 0x00
                        2: Expr
                          head: Symbol .
                          args: Array{Any}((2,))
                            1: Module Memoization
                            2: QuoteNode
                              value: Symbol _static_get_cache
                        3: Expr
                          head: Symbol curly
                          args: Array{Any}((2,))
                            1: GlobalRef
                              mod: Module Memoization
                              name: Symbol Tuple
                              binding: Core.Binding
                                value: #undef
                                globalref: GlobalRef#= circular reference @-2 =#
                                owner: Core.Binding
                                  value: Tuple <: Any
                                  globalref: GlobalRef
                                    mod: Module Core
                                    name: Symbol Tuple
                                    binding: Core.Binding#= circular reference @-2 =#
                                  owner: Core.Binding#= circular reference @-1 =#
                                  ty: Any
                                  flags: UInt8 0x03
                                ty: #undef
                                flags: UInt8 0x00
                            2: Expr
                              head: Symbol call
                              args: Array{Any}((2,))
                                1: GlobalRef
                                  mod: Module Memoization
                                  name: Symbol typeof
                                  binding: Core.Binding
                                    value: #undef
                                    globalref: GlobalRef#= circular reference @-2 =#
                                    owner: Core.Binding
                                      value: typeof (function of type typeof(typeof))
                                      globalref: GlobalRef
                                        mod: Module Core
                                        name: Symbol typeof
                                        binding: Core.Binding#= circular reference @-2 =#
                                      owner: Core.Binding#= circular reference @-1 =#
                                      ty: Any
                                      flags: UInt8 0x03
                                    ty: #undef
                                    flags: UInt8 0x00
                                2: Symbol #21#func
                    3: Expr
                      head: Symbol call
                      args: Array{Any}((3,))
                        1: GlobalRef
                          mod: Module Memoization
                          name: Symbol which
                          binding: Core.Binding
                            value: #undef
                            globalref: GlobalRef#= circular reference @-2 =#
                            owner: Core.Binding
                              value: which (function of type typeof(which))
                              globalref: GlobalRef
                                mod: Module Base
                                name: Symbol which
                                binding: Core.Binding#= circular reference @-2 =#
                              owner: Core.Binding#= circular reference @-1 =#
                              ty: #undef
                              flags: UInt8 0x03
                            ty: #undef
                            flags: UInt8 0x00
                        2: Expr
                          head: Symbol .
                          args: Array{Any}((2,))
                            1: Module Memoization
                            2: QuoteNode
                              value: Symbol _static_get_cache
                        3: Expr
                          head: Symbol curly
                          args: Array{Any}((2,))
                            1: GlobalRef
                              mod: Module Memoization
                              name: Symbol Tuple
                              binding: Core.Binding
                                value: #undef
                                globalref: GlobalRef#= circular reference @-2 =#
                                owner: Core.Binding
                                  value: Tuple <: Any
                                  globalref: GlobalRef
                                    mod: Module Core
                                    name: Symbol Tuple
                                    binding: Core.Binding#= circular reference @-2 =#
                                  owner: Core.Binding#= circular reference @-1 =#
                                  ty: Any
                                  flags: UInt8 0x03
                                ty: #undef
                                flags: UInt8 0x00
                            2: GlobalRef
                              mod: Module Memoization
                              name: Symbol Any
                              binding: Core.Binding
                                value: #undef
                                globalref: GlobalRef#= circular reference @-2 =#
                                owner: Core.Binding
                                  value: Any
                                  globalref: GlobalRef
                                    mod: Module Core
                                    name: Symbol Any
                                    binding: Core.Binding#= circular reference @-2 =#
                                  owner: Core.Binding#= circular reference @-1 =#
                                  ty: Any
                                  flags: UInt8 0x03
                                ty: #undef
                                flags: UInt8 0x00
                2: Expr
                  head: Symbol block
                  args: Array{Any}((2,))
                    1: LineNumberNode
                      line: Int64 172
                      file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
                    2: Expr
                      head: Symbol call
                      args: Array{Any}((3,))
                        1: GlobalRef
                          mod: Module Core
                          name: Symbol eval
                          binding: Core.Binding
                            value: eval (function of type typeof(Core.eval))
                            globalref: GlobalRef#= circular reference @-2 =#
                            owner: Core.Binding#= circular reference @-1 =#
                            ty: #undef
                            flags: UInt8 0x03
                        2: Module Main
                        3: Expr
                          head: Symbol call
                          args: Array{Any}((5,))
                            1: GlobalRef
                              mod: Module Core
                              name: Symbol _expr
                              binding: Core.Binding
                                value: _expr (function of type typeof(Core._expr))
                                globalref: GlobalRef#= circular reference @-2 =#
                                owner: Core.Binding#= circular reference @-1 =#
                                ty: Any
                                flags: UInt8 0x01
                            2: QuoteNode
                              value: Symbol macrocall
                            3: QuoteNode
                              value: Symbol @generated
                            4: QuoteNode
                              value: LineNumberNode
                                line: Int64 172
                                file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
                            5: Expr
                              head: Symbol call
                              args: Array{Any}((4,))
                                1: GlobalRef
                                  mod: Module Core
                                  name: Symbol _expr
                                  binding: Core.Binding
                                    value: _expr (function of type typeof(Core._expr))
                                    globalref: GlobalRef#= circular reference @-2 =#
                                    owner: Core.Binding#= circular reference @-1 =#
                                    ty: Any
                                    flags: UInt8 0x01
                                2: QuoteNode
                                  value: Symbol function
                                3: Expr
                                  head: Symbol call
                                  args: Array{Any}((4,))
                                    1: GlobalRef
                                      mod: Module Core
                                      name: Symbol _expr
                                      binding: Core.Binding
                                        value: _expr (function of type typeof(Core._expr))
                                        globalref: GlobalRef#= circular reference @-2 =#
                                        owner: Core.Binding#= circular reference @-1 =#
                                        ty: Any
                                        flags: UInt8 0x01
                                    2: QuoteNode
                                      value: Symbol call
                                    3: Expr
                                      head: Symbol copyast
                                      args: Array{Any}((1,))
                                        1: QuoteNode
                                          value: Expr
                                            head: Symbol .
                                            args: Array{Any}((2,))
                                              1: Module Memoization
                                              2: QuoteNode
                                                value: Symbol _static_get_cache
                                    4: Expr
                                      head: Symbol call
                                      args: Array{Any}((3,))
                                        1: GlobalRef
                                          mod: Module Core
                                          name: Symbol _expr
                                          binding: Core.Binding
                                            value: _expr (function of type typeof(Core._expr))
                                            globalref: GlobalRef#= circular reference @-2 =#
                                            owner: Core.Binding#= circular reference @-1 =#
                                            ty: Any
                                            flags: UInt8 0x01
                                        2: QuoteNode
                                          value: Symbol ::
                                        3: Expr
                                          head: Symbol call
                                          args: Array{Any}((4,))
                                            1: GlobalRef
                                              mod: Module Core
                                              name: Symbol _expr
                                              binding: Core.Binding
                                                value: _expr (function of type typeof(Core._expr))
                                                globalref: GlobalRef#= circular reference @-2 =#
                                                owner: Core.Binding#= circular reference @-1 =#
                                                ty: Any
                                                flags: UInt8 0x01
                                            2: QuoteNode
                                              value: Symbol call
                                            3: QuoteNode
                                              value: Symbol typeof
                                            4: Symbol #21#func
                                4: Expr
                                  head: Symbol copyast
                                  args: Array{Any}((1,))
                                    1: QuoteNode
                                      value: Expr
                                        head: Symbol block
                                        args: Array{Any}((3,))
                                          1: LineNumberNode
                                            line: Int64 172
                                            file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
                                          2: LineNumberNode
                                            line: Int64 173
                                            file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
                                          3: Expr
                                            head: Symbol call
                                            args: Array{Any}((1,))
                                              1: UnionAll
                                                var: TypeVar
                                                  name: Symbol K
                                                  lb: Union{}
                                                  ub: Any
                                                body: UnionAll
                                                  var: TypeVar
                                                    name: Symbol V
                                                    lb: Union{}
                                                    ub: Any
                                                  body: IdDict{K, V} <: AbstractDict{K, V}
                                                    ht::Vector{Any}
                                                    count::Int64
                                                    ndel::Int64
            3: LineNumberNode
              line: Int64 180
              file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
            4: Expr
              head: Symbol call
              args: Array{Any}((2,))
                1: empty_cache! (function of type typeof(Memoization.empty_cache!))
                2: Symbol f
    7: LineNumberNode
      line: Int64 182
      file: Symbol /Users/cameron/.julia/packages/Memoization/7WxyR/src/Memoization.jl
    8: Symbol #21#func