Defining function inside a macro


#1

What am I doing wrong? It’s giving me a syntax error… Thanks

# @makefn "fn" :hello
macro makefn(prefix, name)
    quote
        fn = prefix * "_" * name
        function $(fn)
            println("hello")
        end
    end
end
syntax: expected "end" in definition of function "($ fn)"

#2

Calculate fn outside of the quote. That piece is calculated at compiled time, and then build the function with the name in it as what should be run at runtime.


#3

Thanks, Chris. I also forgot about the parentheses after the function name. This works:

# @makefn "fn" "hello"
macro makefn(prefix, name)
    fn = Symbol(prefix * "_" * name)
    quote
        function $(esc(fn))()
            println("hello")
        end
    end
end
@makefn "fn" "hello"

#4

Next challenge… Is it possible to define the function with custom number of args? maybe I would have to mess with the AST?

# @makefn "fn" "hello" :a :b :c
# => fn_hello(a,b,c) = sum([a,b,c])
macro makefn(prefix, name, args...)
    fn = Symbol(prefix * "_" * name)
    argstup = Tuple(args)
    quote
        function $(esc(fn))($(esc(argstup)))
            sum([$(esc(argstup))])
        end
    end
end
syntax: "(Expr(:quote, :a)::Any, Expr(:quote, :b)::Any, Expr(:quote, :c)::Any)" is not a valid function argument name

Fast way to access bits of Float64?
#5

Just throw the splatted args into the function’s argument setup at compile time. Use macroexpand to see what it does.


#6

I splatted it three different ways. The first two didn’t get through compilation stage so the last one seems most promising. Read on…

  1. $(esc(args...))
MethodError: no method matching esc(::Symbol, ::Symbol, ::Symbol)
  1. $(esc(args)...)
MethodError: no method matching start(::Expr)
  1. $(esc(args))...
syntax: "(:a, :b, :c)" is not a valid function argument name

Here’s what happens with the 3rd method:

julia> macro makefn3(prefix, name, args...)
           fn = Symbol(prefix * "_" * name)
           quote
               function $(esc(fn))($(esc(args))...)
                   sum([$(esc(args))...])
               end
           end
       end
@makefn3 (macro with 1 method)

julia> @makefn3 "fn" "hello" a b c
ERROR: syntax: "(:a, :b, :c)" is not a valid function argument name

julia> @macroexpand(@makefn3 "fn" "hello" a b c)
quote  # REPL[25], line 4:
    function fn_hello((:a, :b, :c)...) # REPL[25], line 5:
        (Main.sum)([(:a, :b, :c)...])
    end
end

#7
julia> macro makefn(prefix, name, args...)
           fn = Symbol(prefix * "_" * name)
           argstup = Tuple(args)
           quote
               function $(esc(fn))($(map(esc, argstup)...))
                   sum([$(map(esc, argstup)...)])
               end
           end
       end
@makefn (macro with 1 method)

julia> @makefn "fn" "hello" a b c
fn_hello (generic function with 1 method)

julia> fn_hello(1,2,3)
6

#8

Or $(esc.(argstup)...) instead of $(map(esc, argstup)...), works too.