Help with refactoring function into macro

Hi - yet another help request in regards to macros. I’ve been trying multiple approaches to no avail.

Let’s say I have a code like this:

module Z 

export z

function z(m = @__MODULE__, a='a'; c...)
  "$m :: $a & $(c...)"
end

end


module A 

using ..Z 

z(@__MODULE__, 'b', x='x')

end

resulting in "Main.A :: b & :x => 'x'"

I don’t like how I have to pass @__MODULE__ when invoking z and I would like to write a macro Z.@z which uses its __module__ argument and automatically invokes z passing in all the other arguments. So it will be invoked as @z('b', x='x') and will return the same "Main.A :: b & :x => 'x'"

I tried many approaches, but nothing worked (got an error that macros don’t take keyword arguments, I tried sending an expression and manipulating its args, etc). I find it quite tricky.

Thank you

1 Like

No feedback… Wondering if it’s because it’s too simple and I’m missing something… Or too complicated. Curious at least about the best approach, if anybody tried to achieve something similar.

I think this is more or less what you want:

module Z 

export @z

function _z(m = @__MODULE__, a='a'; c...)
    "$m :: $a & $(c...)"
end

macro z(arglist)
    args = []
    for arg in arglist.args
        if isa(arg, Expr)
            argexpr = :($(esc(arg.args[1])) = $(esc(arg.args[2])))
            argexpr.head = :kw
            push!(args, argexpr)
        else
            push!(args, :($(esc(arg))))
        end
    end
    :(_z($__module__, $(args...)))
end

end

module A 

using ..Z 
y = 'x'
w = 'b'

println(@z (w, x=y))

end

Does it work for you?

@mateuszbaran Thank you, it does!

The problem though is that the syntax is very “unjulian” - one has to pass an invalid tuple as an expression - which is very error-prone. The regular ways of invoking macros, which would be either @z('b', x='x') or @z 'b', x='x', both error out. This can be very confusing for a regular user of the API which doesn’t expect that this is, in fact, a special case of an invocation.

I’ve been struggling with this a lot - I’m starting to think that maybe it can’t be done given how the language works.

Do you want something like this?

You can also make @z accept variable numer of arguments, and @z('b', x='x') will work. With some effort, most user errors can be checked for in the macro itself.

Thanks for the tip - can’t tell, at first sight, I need to try it out. But it might work, I didn’t realise one can have @macro(ex, args...) - I tried with both @macro(ex) and @macro(args...) but ultimately failed. This might do the trick.

Yes, I think it’s what @pfitzseb pointed to, right? Is that what you have in mind? Thanks

Yes, something like that :+1:.