Arc Forumnew | comments | leaders | submitlogin
A macrolet implementation
7 points by almkglor 4019 days ago | 13 comments
I've pushed an implementation of CL's macrolet on the arc-wiki.git :

  (def *macrolet-test1 ()
    (macrolet
       (
         (myassign x y) `(= ,x ,y))
       (let z 1
         (prn "z before: " z)
         (myassign z 42)
         (prn "z after: " z)
         z)))
Bug reports and better ways to express the macrolet macro are welcome.


4 points by cooldude127 4019 days ago | link

one suggestion: remove the outer set of parentheses to match a regular let. so do:

  (maclet (myassign x y) `(= ,x ,y)
    (let z 1
      (prn "z before: " z)
      (myassign z 42)
      (prn "z after: " z)
      z))
also, changed the name to maclet to match 'mac' for definitions. if necessary, also define a macwith like the regular with.

-----

2 points by almkglor 4019 days ago | link

As it is, macrolet is effectively a 'macwith.

-----

1 point by cooldude127 4018 days ago | link

yes i know, i would say this version should be macwith, and a version with less parentheses should be maclet. sorry if i wasn't clear.

-----

2 points by almkglor 4018 days ago | link

Yes I know too. Am currently thinking of overloading 'mac, such that:

  (mac:let (myassign x y) `(= ,x ,y)
    (let z 1
      (prn "z before: " z)
      (myassign z 42)
      (prn "z after: " z)
      z))
and:

  (mac:with
     (
       (myassign x y) `(= ,x ,y)
       (mysref s x i) `(= (,s ,i) ,x))
     (let ...))

-----

2 points by nlavine 4018 days ago | link

That is certainly the arc way to do it, but there's one thing I'm not sure about: do you want mac:let or let:mac? Or, in fact, would either one work? The problem I'm trying to get at is what happens to let's three arguments (symbol, value, body) when you compose mac with it. It seems like you really want mac to apply to only the value argument, which is not a straight composition.

I almost hate to get into this, but it seems to me that the real solution is first-class macros:

  (let my-macro (mac (x) `(prn ,x))
    ...)
I believe pg said that one of the things he gave up in the current implementation that he'd really like to have is first-class macros, and it may be that we can't get them in mzscheme without unacceptable performance loss, so perhaps maclet is the temporary solution, but this seems like the long-term way to go.

-----

2 points by absz 4018 days ago | link

How about

  (mac mc args
    `(annotate 'mac (fn ,@args)))
? Then

  arc> (= m (mc (x) `',x))
  #3(tagged mac #<procedure>)
  arc> (m not-a-declared-symbol)
  not-a-declared-symbol
. Of course, the downside here is that, as you observe, we can't do

  arc> (let m (mc (x) `',x) (m not-a-declared-symbol))
  Error: "reference to undefined identifier: _not-a-declared-symbol"
or

  arc> ((mc (x) `',x) not-a-declared-symbol)
  Error: "reference to undefined identifier: _not-a-declared-symbol"
. My understanding is that the problem with the second is that (annotate 'mac ...) is not evaluated before not-a-declared-symbol:

  arc> ((mc (x) `',x) 'not-a-declared-symbol)
  (quote not-a-declared-symbol)
. We get the same output in the other case:

  arc> (let m (mc (x) `',x) (m 'not-a-declared-symbol))
  (quote not-a-declared-symbol)
. However, here I'm not sure why. So after working through this, I'm not actually sure what these results signify. But here they are. Make of them what you will.

-----

1 point by cadaver 4018 days ago | link

There was a post a while ago about how you might get first-class macros through lazy compilation: http://arclanguage.com/item?id=842

I'm not exactly sure if this is what you want. I believe there is a difference between macros that are only expanded once (in lazy compilation), and macros that are called each time the macro-expression is encountered. Though the latter case would only make sense if the macro exhibits side-effects, or depends on side-effects, or if the argument list to the macro may change. Not sure whether there is any benefit in that.

-----

2 points by cooldude127 4018 days ago | link

first-class macros would obviously solve this problem completely, that same way scheme eliminates the need for CL's flet and labels. it would be nice if we could just assign anonymous macros to bindings, but we can't do that yet.

the problem is that the composition just doesn't really make sense in this context, as you said. it doesn't translate the way composition should. in my opinion, maclet is deserving of it's own form.

-----

1 point by almkglor 4018 days ago | link

No, what I mean is, to overload mac:

  (let oldmac mac
   (= mac
        (annotate
           (fn x
              (if (and (is 1 (len x)) (acons (car x)))
                 (my-code ...)
                 (apply oldmac x)))
           'macro)))

-----

2 points by cooldude127 4018 days ago | link

i'm not certain whether this would be abuse of function composition or not. for some reason, maclet and macwith just seem more appropriate than mac:let and mac:with. also seems sort of dangerous to override such an important construct.

-----

1 point by almkglor 4018 days ago | link

My concern is largely namespace pollution. By overloading (NOT overriding) 'mac, we squeeze this feature into a smaller namespace, adding 0 new macros ^^. In any case, the arc-wiki 'breakable and 'p-m macros have a similar intended use too ^^.

-----

1 point by nlavine 4018 days ago | link

It does seem like you're going to want to do a lot of macro composition where you only compose over the body argument. Is it possible to make this work? The most obvious way for me is to use the single-argument function syntax, but somehow modified for macros, like this:

([let x 3 _]:pm:body)

This is just a really quick thought. Any comments?

-----

1 point by almkglor 4018 days ago | link

Sorry, I don't understand your question. ~.~? !.! T.T

Internally, Arc transforms (foo:bar niz) to (foo (bar nitz)), so if 'foo is a macro, it will see an argument of (bar nitz)

-----