Arc Forumnew | comments | leaders | submitlogin
A solution for using macros with namespace modules (awwx.posterous.com)
2 points by aw 4985 days ago | 11 comments


1 point by Pauan 4985 days ago | link

Nice to see you (re)discovering Arubic modules. :D Mind if I hack it a bit? I'll probably end up changing the names of the functions, and also smooth it over.

I actually had already experimented with using the function's value:

  (mac foo ()
    `(,bar 1 2 3))

  ; rather than this:

  (mac foo ()
    `(bar 1 2 3))
But then you end up needing to unquote all function symbols... combined with using gensyms, this makes writing macros clunkier than it should be, I think.

So then I was mulling over some alternatives. This system looks good to me: http://arclanguage.org/item?id=9024

This lets you get unhygienic macros if you want to, but makes writing hygienic macros really easy. I think that's the way it should be. Unhygienic macros don't play well with modules, so if you want them, you'll have to be aware of the pitfalls. So rather than jumping through hoops trying to make unhygienic macros work with modules... let's make hygienic macros really nice and easy to use.

So... crazy as it may be, I considered the idea of changing the meaning of quasiquote, unquote, and unquote-splicing:

  `(foo 1 2 3)  -> (eval '(foo 1 2 3) <environment>)
  `(foo ,1 2 3) -> (eval '(foo (eval 1 (current-environment)) 2 3) <environment>)
Basically, when you quasiquote something, it's always evaluated in the environment it was defined in... but if you unquote something, it'll be evaluated in the caller's environment. Which is the opposite of what it does right now.

Of course, we could also keep the current meaning of quasiquote etc. and come up with new symbols for the hygienic quasiquote. Or perhaps we could do something like this:

  (hygmac foo ()
    `(bar 1 2 3))
Which uses a hidden `let` to shadow the definition of quasiquote... then quasiquote would have special meaning within hygmac, but be normal everywhere else. I like that idea.

-----

1 point by Pauan 4985 days ago | link

Also, in line with the whole "hacking on ar" thing, mind if I add in some Arubic-y things, at least the ones that are backwards compatible with pgArc? Like... message passing, for instance. Or keyword args (though that's not 100% backwards compatible, it's really close[1]).

---

* [1]: If somebody happens to use a symbol that starts with a colon, then keyword args could change the meaning of their program. But I wouldn't expect symbols prefixed with : to be very common.

-----

1 point by aw 4985 days ago | link

Of course you can make and publish any changes you want, it's open source so you don't need my permission to do anything :-). Perhaps you're asking whether I'll merge your changes into my version? A goal of ar to make Arc more hackable, so ideally ar would be powerful enough that someone could implement e.g. keyword arguments as a library. Smaller changes will often be easier to digest, so saying "I could implement keyword arguments if only ar had X" is more likely to win me over to thinking about the best way to add X to ar.

With respect to backwards compatibility... since ar is supposed to be hackable, one of the ways it should be hackable is to get it to produce a runtime that is mostly compatible with Arc 3.1. But on the other hand, if someone wants to implement a language which is completely incompatible with Arc 3.1, they should be able to do that as well.

-----

1 point by Pauan 4985 days ago | link

Yeah, I was asking more along the lines of "merging into official ar"; obviously I can do whatever I want in my personal fork. Or with Arubic.

---

"[...] ideally ar would be powerful enough that someone could implement e.g. keyword arguments as a library."

Would be awesome if we could figure out a way to do that.

---

""I could implement keyword arguments if only ar had X" is more likely to win me over to thinking about the best way to add X to ar."

I'll give some thought about the precise requirements for keyword args. Message passing too.

---

"With respect to backwards compatibility... since ar is supposed to be hackable, one of the ways it should be hackable is to get it to produce a runtime that is mostly compatible with Arc 3.1. But on the other hand, if someone wants to implement a language which is completely incompatible with Arc 3.1, they should be able to do that as well."

Yes, but if the changes need to be done in the core (and not as a library on top of the core), then backwards compatibility does become an issue, since you've already stated you're trying to be mostly-compatible with pgArc. Ideally, any backwards incompatible changes will be done to make ar as hackable as possible, lessening the need to change the core in the future.

-----

1 point by aw 4985 days ago | link

> "[...] ideally ar would be powerful enough that someone could implement e.g. keyword arguments as a library."

Would be awesome if we could figure out a way to do that.

The compiler is reflected into Arc so if all you need to do is make compiler changes you can do that by modifying "ac" or the functions it calls such as "ac-call". If the feature also needs to make runtime changes then we might need to add some hooks to the runtime to enable that.

-----

1 point by Pauan 4985 days ago | link

Hm... interesting. In that case, I might be able to implement Arubic as a bunch of extends and maybe some supporting functions... then Arubic would basically be a thin skin over ar. I'll look into it.

-----

1 point by aw 4985 days ago | link

This is a goal of ar: I don't know if it's actually possible or not, but ideally you should be able to implement Arubic as a library on top of ar :-)

-----

2 points by bogomipz 4985 days ago | link

The problem with using function values is that the macro then remembers the original implementation even if the function is redefined.

-----

1 point by aw 4985 days ago | link

Yes, once a macro has been used then redefining helper functions won't affect code which has already been compiled. (New or reloaded code which uses the macro will get the redefined helper function).

It makes the helper functions act like macros in that way: redefining a macro won't affect code which has already been compiled either.

-----

3 points by rocketnia 4984 days ago | link

I like your approach a lot, aw, but I too noticed this issue. I think it can be overcome just by changing the idiom slightly.

First, some definitions:

  (def fn-latemac (mac-getter)
    (annotate 'mac
      (fn args `(,(mac-getter) ,@args))))
  
  ; Not to be hypocritical, let's implement 'latemac as if by using
  ; 'latemac.
  (mac latemac (globalvar)
    `(,(fn-latemac:fn () fn-latemac) (fn () ,globalvar)))
Now we can define your example 'foo and 'bar like this:

  (mac foo ()
    `(,latemac.do
       (,latemac.prn "this is macro foo expanding into bar")
       (,latemac.bar)))
  
  ; Oops, now let's define 'bar. Go go, late binding!
  (mac bar ()
    `(,latemac.prn "yo, this is macro bar"))
As you can see below, this already works in the current version of ar. ^_^

(For future reference, I'm using https://github.com/awwx/ar/commit/c375d77bd2dbf6ca8850fda5f6.... )

  arc> (foo)
  this is macro foo expanding into bar
  yo, this is macro bar
  "yo, this is macro bar"
  arc>
    (def bar ()
      (prn "hi, this is bar"))
  *** redefining bar
  #<procedure:g1590>
  arc> (foo)
  this is macro foo expanding into bar
  hi, this is bar
  "hi, this is bar"
Essentially, these are hygienic macros at this point. They use the syntactic closure technique for hygiene, in this case implemented using regular old lambda closures. ^_^ A syntactic closure approach has undesirable properties like making the expansion hard to code-walk or serialize, but those activities seem rare enough anyway, probably because they're already difficult. I think this'll do just fine as far as modules go.

-----

1 point by aw 4985 days ago | link

(note that this is completely different than my experiment yesterday with Arc-style macros in Racket, in case you were wondering)

-----