Arc Forumnew | comments | leaders | submitlogin
Why must Arc have mac?
2 points by lark 4476 days ago | 11 comments
If the distinguishing feature of macros is that a backquote ` generates code, is it necessary for a Lisp to have both defun and defmacro (or both def and mac)?


5 points by akkartik 4476 days ago | link

Backquote is just a helper to create list templates. You can use it in either def or mac.

  (def foo1(a) (list '+ a 1))

  (def foo2(a) `(+ ,a 1))

  (mac foo3(a) `(+ ,a 1))

  (mac foo4(a) (list '+ a 1))

  arc> (foo1 3)
  (+ 3 1)
  arc> (foo2 3)
  (+ 3 1)
  arc> (foo3 3)
  4
  arc> (foo4 3)
  4
The key feature of macros isn't backquote. It is that they don't eval their args, and that they eval their result.

-----

1 point by Pauan 4476 days ago | link

To expand a little on what akkartik's saying...

The point of functions (def) is to take evaluated arguments as input and spit back a result as output.

The point of macros (mac) is to take unevaluated arguments as input and spit back a list which will then be evaluated.

---

The reason for this is to allow us to do things we cannot do with functions. For instance, consider the "let" macro:

  (mac let (x y . body)
    `((fn (,x) ,@body) ,y))
Now you can call it like so:

  (let a 5 a)
Which returns 5. Now, what if we replace "mac" with "def"?

  (def let (x y . body)
    `((fn (,x) ,@body) ,y))
If we now call (let a 5 a) it will throw the error "the variable a is undefined". Why? Because functions evaluate all of their arguments before calling the function. Which means that it tries to evaluate the argument "a", but that variable isn't defined.

But macros don't evaluate their arguments, which is why "let" works.

-----

1 point by Pauan 4476 days ago | link

To expand on this further, there's also a thing known as "vau" which are basically just functions that don't evaluate their arguments. If Arc had vaus, you might write "let" like this:

  (vau let (x y . body) env
    (eval `((fn (,x) ,@body) ,y) env))
Notice that this is exactly like the macro version, except that it has an extra argument "env" and it calls eval explicitly. Every macro can be easily rewritten into a vau, and in fact you can even implement "mac" using vau:

  (vau mac (name args . body) env
    (w/uniq e
      (eval `(vau ,name ,args ,e
               (eval (do ,@body) ,e))
            env)))
What's the difference between vau and mac, then? They both receive their arguments unevaluated, but:

1) Vaus also receive an extra environment argument.

2) Vaus return a value, just like functions, which means you need to call eval explicitly. Macros, however, return a list which is then evaluated automatically.

3) Macros (conceptually) run at compile-time, but vaus (conceptually) run at run-time, just like functions.

Vaus are a much cleaner and more consistent system, especially since it's trivial to define "fn" in terms of vau, but you can't do that with macros. But macros have the benefit that they can be run at compile-time, so in Arc they would be faster than vaus.

-----

2 points by lark 4471 days ago | link

It's surprising that macros destroy functions rather than generate them.

Thanks for vau. It seems it's defun (and def) that are redundant. Compile-time can be modeled as run-time where t = 0, which means one could have macros at run-time. Some such macros could choose to not evaluate their results.

-----

1 point by Pauan 4471 days ago | link

I don't understand what you mean by macros "destroying" functions. They are completely different concepts. They behave differently and they are used for very different purposes: they are orthogonal.

---

It is possible to design a Lisp that doesn't have a function/macro distinction. Such a Lisp would just use vau for everything. An example is the language Kernel:

http://web.cs.wpi.edu/~jshutt/kernel.html

So, to answer your original question: no, it is not necessary to have both mac and def, but that's how most Lisps (including Arc) do it. I think the primary reason is historical, since I think the speed argument is actually bogus for most programs.

-----

2 points by lark 4470 days ago | link

I meant the biggest casualty of vau (which is the ultimate macro) is def.

-----

1 point by Pauan 4470 days ago | link

Well, I'm not sure I'd say that... what vau does is remove the need for mac, but def is still there, to make it more convenient to define functions.

That does mean that functions can be implemented in user-land, which is very nice, but it doesn't really change functions very much.

---

Also, I prefer to not think of vau as being "the ultimate macro", I prefer to see it as a construct which is very different from macros, but happens to serve the same purpose in most cases, which is why vau can replace macros in most (but not all!) cases.

Then again, I also prefer to see functions as being separate from vaus, even if they're implemented using vau... So take what I say with a grain of salt.

-----

2 points by akkartik 4470 days ago | link

It took me a while to realize that I shouldn't think of vau as a macro-like thing. Macros and functions are different kinds of evaluation policies; vau just lets you construct arbitrary evaluation policies. It's like discovering the quarks that all fundamental particles are made up of; quarks don't preferentially constitute an electron more than a neutron.

The challenge now is to build a runtime where vau can have performance comparable to existing lisp systems.

-----

1 point by Pauan 4470 days ago | link

"It took me a while to realize that I shouldn't think of vau as a macro-like thing."

Welcome to the club. Glad you could join us.

---

"The challenge now is to build a runtime where vau can have performance comparable to existing lisp systems."

Yeah. I think a Lisp without implicit late binding (like Nulan) should have a much easier time at it, though.

-----

1 point by lark 4471 days ago | link

> The point of functions (def) is to take evaluated arguments as input and spit back a result as output.

That doesn't have to be the point. They could instead take unevaluated arguments and evaluate them only if the user wants them to be evaluated.

-----

1 point by Pauan 4471 days ago | link

That's what vaus do... which I already covered in my second post. The word "function" in Scheme, Common Lisp, JavaScript, Python, Ruby, Lua, etc. all refer to something that always evaluates its arguments. Arc is no different.

-----