Search code examples
macrosjulialisp

Julia equivalent of a lisp symbol macro?


I'm trying to do what a Lisp hacker would call a "symbol macro". To wit, here's what I'm using now:

global ghypers = Dict()
macro hyp(variable, value); ghypers[:($variable)] = :($value); end
macro hyp(variable); ghypers[:($variable)]; end
@hyp foo 5
println(ghypers)
println(@hyp foo)
println(@hyp(foo)+1)

So far so good, but the last thing is ugly, I want to do this:

@foo+1

Sort of like this:

macro foo(); ghypers[:foo]; end
println(@foo)
println(@foo()+1)

Close, and works, but not quite what I wanted, which, again, is:

println(@foo+1) 
MethodError: no method matching @foo(::LineNumberNode, ::Module, ::Expr)

Except that that doesn't work.

Lisp has the concept of a symbol macro, where you can bind a macro expansion to a symbol, like foo, so that symbol would expand the to value.

Now, an obvious but equally bad (in the unhygienic sense) way to do this would simply be to bind the global var foo to the value, but I don't want global (or, rather, I want them localized in ghypers).

Is there any way to do the thing I'm seeking in Julia?

(<flame> Having been a happy lisp camper for 40+ years, Julia is the first programming language that I can say I actually like even a little. But not having grok'ed the importance of homoiconicity, their macro system is ... well, to my eyes, quite a mess, where Lisp's is clear as rain water. </flame> :-)


Solution

  • There are two direct ways. If the thing is hygienic and referentially transparent, just use a const value. That won't work in your example.

    Otherwise you have to use the macro in call syntax: @foo(). There's no around that, it's how Julia syntax work. Although I won't recommend doing it either.

    But a nicer alternative, IMHO, would be an "anaphoric context macro", something like:

    @withhyper (stuff) begin
        println(foo+1) 
    end
    

    where foo is a name escaped inside some local scope. Expand the block to a variation of

    let foo = setup_hyper(stuff, ...)
        println(foo+1)
    end