How to write a recursive macro?

I came across some online posts about implementing short-circuiting logical operators as macros in Lisp, and I’m wondering how to do the same in Julia. I’m trying to write a vararg macro @or such that

@or expr1 expr2 expr3 expr4

expands into

if expr1
    true
else
    @or expr2 expr3 expr4
end

The recursion should end when there is only one argument, i.e.

@or expr1

should expand into simply expr1.

I made some attempts but got confused about how to interpolate vararg tuples in quote expressions. Any ideas?

This is not recursive, however it seems to do what you want.

ior(x,y) = x || y
macro or(xs...)
    :(foldl(ior, $xs, init=false))
end
julia> @or(false), @or(true)
(false, true)

julia> @or(false, false), @or(true, true)
(false, true)

julia> @or(true, false), @or(false,true)
(true, true)

Thanks, but I was actually trying to avoid the built-in || operator and re-implement it using only the if-else clause, just for fun, following this Stack Overflow Post in Lisp.

Like this?

macro or(args...)
    length(args) == 1 && return only(args)
    quote
        if $(esc(args[1]))
            true
        else
            @or $(args[2:end]...)
        end
    end
end
julia> @or 1 == 2 1 == 3 1 == 1
true

julia> @macroexpand @or 1 == 2 1 == 3 1 == 1
quote
    #= Untitled-2:150 =#
    if 1 == 2
        #= Untitled-2:151 =#
        true
    else
        #= Untitled-2:153 =#
        begin
            #= Untitled-2:150 =#
            if 1 == 3
                #= Untitled-2:151 =#
                true
            else
                #= Untitled-2:153 =#
                1 == 1
            end
        end
    end
end

Probably the escaping isn’t quite right :slight_smile:

1 Like

That works! A little glitch is that I tried to add esc to your args[2:end] but that breaks the interpolation. I’ll be very interested if someone knows how to do this.

OK, here’s a simple modification of @jules’s code to impose hygiene.

macro or(args...)
    args_esc = esc.(args)
    length(args_esc) == 1 && return only(args_esc)
    quote
        if $(args_esc[1])
            true
        else
            @or $(args_esc[2:end]...)
        end
    end
end