Arc Forumnew | comments | leaders | submitlogin
2 points by fallintothis 5383 days ago | link | parent

They're what first-class functions are to functions. That is, the ability to create anonymous macros and pass them around as first-class objects. Imagine you could do something like

  (let m (macro (x) `(foo ,x))
    ...)
or

  (map (macro (x) `(= ,x 'blah)) xs)
It's like the difference between having fn and just having def. Right now we only have mac and no way to reference macros as their own entity.

  arc> ((annotate 'mac (fn (x) x)) 'x)
  Error: "Function call on inappropriate object #(tagged mac #<procedure>) (x)"
Whenever the topic resurfaces, the word seems to be the same: they still make compilation difficult (the most recent research only seems to be from 2000 or so http://lambda-the-ultimate.org/node/2987) and pg has yet to comment on them. So to implement first-class macros currently, you'd probably effectively turn ac into an interpreter so that macros could expand at run-time.


1 point by aw 5383 days ago | link

I don't understand your map example. Can you give an example of what xs could be and what the resulting output would be?

-----

2 points by fallintothis 5383 days ago | link

That example specifically? It was nonsense. My point was that you could, say, map macros (anonymous or not). Those used to inline functions, for instance: you want to use a macro like a function, but it's a macro for efficiency. Currently, you have to wrap it in a function, which is kludgey.

Silly example:

  ; instead of

  (map complement list-of-fns)

  ; you have to do

  (map [complement _] list-of-fns)
Actual example (news.arc and the karma macro):

  (def leading-users ()
    (sort (compare > [karma _]) ; instead of (compare > karma)
          (users [and (> (karma _) leader-threshold*) (~admin _)])))
Again, macros being first-class intuitively works like functions being first-class. So, with first-class macros you could have expressions return macros.

Silly example:

  ((if (mem 'x stuff) pull push) 'y stuff)
Actual example (my trace library http://www.arclanguage.org/item?id=10372):

  (mac m ...)

  ((traced m) ...) ; traced is a higher-order function
                   ; since m is a macro, (traced m) returns a macro
More popularly, Bawden's paper (http://people.csail.mit.edu/alan/mtt/) demonstrates writing a module system with first-class macros in which the modules are also first-class.

-----

2 points by shader 5383 days ago | link

I think the example was supposed to set all of the variable names contained in the list xs to contain the value 'blah.

In reality, you're more likely to want to take in two lists, one of variable names, and one of values, and assign each value to each name.

-----

1 point by fallintothis 5383 days ago | link

I think the example was supposed to set all of the variable names contained in the list xs to contain the value 'blah.

The example wasn't supposed to do anything. I was just foo-bar-baz-ing something. I don't think the example would actually do that anyways, for the same reason the following doesn't work.

  arc> (mac m (x) `(= ,x 'blah))
  #(tagged mac #<procedure: m>)
  arc> (m 'a)
  Error: "reference to undefined identifier: _quote"
I guess it would've alleviated confusion had I said

  (map (macro (x) ...) xs)

-----

2 points by shader 5383 days ago | link

Well, first class macros don't work right now anyway, so I wasn't expecting your example to work. I was just trying to figure out the idea behind the example.

And I think that first class macros would be required to set an arbitrary list of variables to contain an arbitrary list of values via map. Obviously with or let would work in a local scope, but if you want global variables you'd need something like that, or at least a function that wraps the macro.

-----

1 point by aw 5383 days ago | link

Oh, do you mean you want access to the lexical environment at run time? That sounds like to me more like you want the lexical environment to be a first class object.

-----

2 points by shader 5383 days ago | link

The lexical environment is (mostly) accessible at run time. The problem is when you're returning a macro from a function, instead of just renaming it.

Suppose you implement packages as tables of symbols. Suppose also that you have a macro stored in said package. There are two ways to use the macro

1.

  (= newmac package!newmac)
  (newmac args)
2.

  (package!newmac args)
I think that most people would like to use the second form, because it doesn't require importing the macro into the current namespace. Unfortunately, the second version requires first class macros.

Obviously, since the compiler can't easily determine that an expression is going to be a macro, it won't have the advantage of compile time optimization. I'm not sure how important that is. Given that arc is already a "slow" language, why not give it more expressive power at the cost of slightly lower speed? Especially since the user can choose whether or not to use them.

So, as I see it first class macros give you several features:

1. easy to make a package system that can contain macros 2. macros fit in better with functions; no wrapping of macros in functions 3. anonymous macros created for mapping across expressions, etc.

-----

2 points by aw 5383 days ago | link

Oh, I think I sort of get it now...

In an interpreter,

  (expr1 expr2 expr3)
evaluation of expr2 and expr3 can be delayed until after expr1 is evaluated. If expr1 turns out to evaluate to a macro, then the literal expr2 and expr3 can be passed to the macro.

-----