Arc Forumnew | comments | leaders | submitlogin
1 point by CatDancer 5678 days ago | link | parent

The first step is to start with an example:

  (= a 5)
  (= b 10)
  (mz (+ ,a (* ,b 2))
Now, what would you like this macro expression to expand into? Something like this?

  (with (x1 a x2 b)
    (ac-scheme (+ x1 (* x2 2))))
(yeah, this morning I'm trying out the name "ac-scheme" for "ac-tunnel" :-)

Now, let's say that expansion is what you want. So your next step is to write a program that takes

  (+ (unquote a) (* (unquote b) 2))
and turns it into

  (with (x1 a x2 b) (ac-scheme (+ x1 (* x2 2))))
this part has nothing to do with macros; you're just creating a list from another list.

  arc> (myprogram '(+ ,a (* ,b 2)))
  (with (x1 a x2 b) (ac-scheme (+ x1 (* x2 2))))
note that's a regular quote ' character there, not a backquote `. We're just feeding a regular old list to myprogram, and it's giving us a regular old list back.

OK! Got that program written? Now the macro is easy:

  (mac mz (x)
    (myprogram x))


1 point by shader 5677 days ago | link

I'm aware of the process for making macros, however my attempts at writing the transformation itself have been rather unsuccessful.

What I originally tried was transforming

  (mz (+ 3 ,a))
into

  (mz (+ 3 __a))
But I couldn't get it to work. The problem is that I seem to need two levels of transformation and evaluation, but macros only give you one.

What is the reason that 'eval is so looked down upon? Isn't that pretty much what macros do themselves? How hard would it be to get an eval that could use the current local scope?

-----

3 points by rntz 5677 days ago | link

    (def transform (e)
      (if atom.e e
          (is car.e 'unquote) (ac-scheme.ac-global-name cadr.e)
          (map transform e)))

    (mac mz body
      `(ac-scheme (begin ,@(map transform body))))
For example:

    arc> (= e 'foo)
    foo
    arc> (mz (symbol->string ,e))
    "foo"

-----

1 point by shader 5677 days ago | link

Nice. Say, that brings up a thought I had earlier.

What if we made that pattern into a function called "maptree". I've needed something like that several times:

  (def maptree (f tree)
    (map (afn (node)
             (if atom.node
                 (f node)
                 (map self node)))
         tree))
It should apply f to each node of the tree, preserving the tree structure. Seems pretty useful to me, if you're going to be doing much transformation of arbitrary list structures.

-----

1 point by shader 5677 days ago | link

In retrospect, it's not much good for transforming the structure, but great for many other things.

For everything else, the other tree functions like trav, treewise, ontree, etc. might be more useful.

How would you make a more general purpose version of maptree that could encompass your transform function? It needs to be able to selectively modify some branches of the tree, and avoid calling map on them. Mine can't do that because it restricts you to atoms. Maybe if it took in two functions, one that operates on atoms, and one on lists, which can then optionally continue the mapping on a subnode. But then what has been gained? You're pretty much writing the whole pattern over again anyway.

Hmm. There seems to be a pattern here, but I can't see how to abstract it out, beyond the somewhat useful but restricted maptree posted above. Ideas?

-----

1 point by rntz 5677 days ago | link

'eval is not particularly looked down on, it's just rather inflexible. Given the way arc works, an 'eval that can use the current local scope is impossible - because arc compiles down into scheme, and an 'eval in arc compiles down to an 'eval in scheme, and an 'eval in scheme cannot use the current local scope. In order to get such an eval, you'd need to rewrite arc as an interpreter.

Edit: Unless mzscheme itself has some special 'eval with that functionality. I didn't think of that. I don't think it does, though.

-----

1 point by shader 5677 days ago | link

I guess I need to study how arc works a little more before I make any bold statements.

Couldn't you just apply the arc compiler/interpreter functions directly to a form? Didn't the parser.arc library do something like that?

-----

1 point by conanite 5677 days ago | link

parser.arc (at least, the parser lib I wrote; there are at least two others in anarki) uses coerce to get corresponding atoms from the token in string form. 'eval would probably have worked too. Otherwise, parser.arc just returns the forms it reads in, only transforming bracket syntax and string interpolations.

-----

1 point by CatDancer 5677 days ago | link

You actually only want one underline:

  arc> (= a 5)
  5
  arc> (ac-scheme _a)
  5
The ac-global-name function will prefix a symbol with an underline for you to create the global symbol name.

As for getting at the lexical scope, with eval or something else, that's up to the Scheme implementation. You'd need to go looking in the MzScheme documentation, or ask on the MzScheme user mailing list, to find out if there's some way to do that. Though why would you need that for this?

-----

1 point by shader 5677 days ago | link

This is what I got, using your mz patch:

  arc> (= a 5)
  5
  arc> (mz (+ 3 _a))
  Error: "reference to undefined identifier: _a"
  arc> (mz (+ 3 __a))
  8
So as far as I can tell, it's two, at least in Anarki on mzscheme 360.

-----

1 point by CatDancer 5677 days ago | link

Oh, I'm running arc3.

So, anyway, when you say you couldn't get "it" to work, what is the "it" that you're trying to do? I thought you were saying you didn't know how to transform "(mz (+ 3 ,a))" into "(mz (+ 3 __a))", but you say you know how to do that, so what is the it that's not working?

-----