
A Codewalk for Arc3's first-class(runtime) macros - wsxiaoys
http://mzh.im/2012/05/28/a-codewalk-for-arc3s-first-class-macro-function-as-macro/
======
dpkendal
Actually macros are not first-class in Arc (though they are stored in the same
namespace as everything else, unlike in Scheme). You can't return one from a
function, for instance. They were a feature of early implementations (they're
mentioned in various places in old stuff related to its development), but they
were cut before the first release. When I asked pg why he removed them, his
answer was:

> I don't remember. It was either because they were useless or confusing or
> intellectually inelegant.

Having experienced first-class macros in my own Lisp implementation, I'm
inclined to agree. Though there are various promises of efficient
implementations scattered through the internet, nobody actually goes into
enough detail to explain it. Non-first-class macros are the way to go for
efficiency, at least, and first-class macros themselves are fairly useless.

~~~
zck
Well, here's an example where _not_ having first-class macros is confusing:

 _in_ takes any number of arguments, and returns whether the first argument is
repeated in the rest of them:

    
    
      arc> (in 2 1 2 3)
      t
      arc> (in 0 1 2 3)
      nil
    

Ok, let's say I have a list, and I want to find out whether something is in
it:

    
    
      arc> (apply in 2 (list 1 2 3))
      Error: "Function call on inappropriate object #(tagged mac #<procedure: in>) (0 1 2 3)"
    

Oh dear. That's not right.

The solution, at least in Arc, is to use _mem_ :

    
    
      arc> (mem 2 (list 1 2 3))
      (2 3)
    

It returns the first sub-list of its second argument such that the car of the
returned list is the element searched for -- or 'nil if the element isn't in
the list. So yes, you can make it work, in this case. But it's surely more
confusing than having _(apply in 2 (list 1 2 3))_ work.

~~~
akkartik
Oh you picked a doozie. Apply won't work on macros even if you have first-
class macros. Even kernel says, "apply expects an applicative." I spent weeks
understanding why. The reason has to do with apply's special status as a dual
of eval, and the fact that macros implicitly eval their result in the caller's
lexical scope. Here's an example to think about:

    
    
      (mac foo(a b) `(cons ,a ,b))
      (apply foo '(1 (2 3)))
      => (cons 1 (2 3))
      => error: 2 is not a function
    

Further discussion (more than you could ever want):
<http://arclanguage.org/item?id=15659>; <http://arclanguage.org/item?id=15907>

~~~
Patient0
I guess the alternative would be to define "apply" as a macro which evals the
cons of its first argument and the other list of arguments...

~~~
akkartik
I addressed this approach at the top of the second link:
<http://arclanguage.org/item?id=15907>

Basically you'd have to:

1\. eval the list

2\. cons the macro to the list, eval to yield the macroexpansion

3\. implicitly eval the macroexpansion -- but _not eval the elements of the
spliced in list_ because they've already been eval'd once.

Step 3 throws a spanner in the works.

So you might say, fine, let's quote all the elements of the spliced in list.
And that'll seem to work for the previous example, but you'll eventually run
into an example like this:

    
    
      (apply = '(x 3))
      => (= 'x '3)
      => error: can't assign to (quote x)
    

So you have to perform step 3 _without actually inserting a literal quote_. I
gave up at this point.

~~~
Patient0
Ah I see.

I've started implementing a Lisp interpreter in C# and I also was going along
the path of making everything "first class" by using f-expressions where
appropriate. So far my only actual f-expressions are "lambda" and "if" -
everything else is bootstrapped as macros or functions.

My "apply" currently expects a function but I was toying with making it "eval"
the cons macro... but then "apply" has to become an f-expression instead of
just a plain normal function which seemed inelegant so I left it as a function
that requires a function.

Reading your story... I think I'll leave it permanently side-stepped!! :-)

~~~
akkartik
Nice to meet ya! I've been working on a lisp as well:
<http://github.com/akkartik/wart#readme>

------
leppie
There is something very wrong with the concept of 'runtime' macros. How is a
'runtime' macro any better than a function/procedure? TBH, I have no idea what
a use case would be for a 'runtime' macro. A macro is generally a compile-time
source transformation. If you need that at runtime, use EVAL.

~~~
Patient0
To me it's because they provide a simple way to solve the hygiene problem in
an interpreter.

That is, it's easier to implement first-class macros (which allow hygiene) in
an intepreter , than it is to implement scheme-style non-first-class hygienic
macros...

I only say this because I know how to implement first-class macros, but I
don't really understand how Scheme's hygienic macros work...

