Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

A "killer" macro

158 views
Skip to first unread message

Eli Bendersky

unread,
Sep 12, 2007, 2:59:19 AM9/12/07
to
Hello all,

In short: I'm looking for a "killer" macro - a macro (or a couple of
related ones) that show what Lisp can do and other languages (those
without uniform syntax) can not.

A longer version: Many contemporary languages boast their sharing most
of the functional features of Lisp. Perl, Ruby, Javascript - all have
convenient lists (or arrays), functions as first class objects,
garbage collection, dynamic typing, lexical closures. However, one
thing they lack is uniform syntax, and hence the service of a powerful
built-in macro system such as Common Lisp's "defmacro".
When confronted by fellow programmers with the question "so why is
Lisp so special", I'm looking for that short - not too hard to
understand - snippet of code that will show them *why*. I'm convinced
that such a snippet must involve macros. But all the examples I ran
into so far have been either too simple - and could be easily
implemented another way (for example with Ruby's blocks), or too
complicated.

Any suggestions for such an example ?

Eli

Christophe

unread,
Sep 12, 2007, 3:30:30 AM9/12/07
to

Hello,

Quickly, Macro in Lisp is, in fact, a way to create your own domain
specific language.

No needs to search a long time : loop, iterate or defclass are smart
examples.

Regards,

Christophe

Pascal Costanza

unread,
Sep 12, 2007, 4:28:32 AM9/12/07
to

Here is my best attempt so far:
http://p-cos.blogspot.com/2007/02/what-is-point-of-macros.html


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/

Thomas F. Burdick

unread,
Sep 12, 2007, 5:51:12 AM9/12/07
to
On Sep 12, 10:28 am, Pascal Costanza <p...@p-cos.net> wrote:

> Eli Bendersky wrote:
>
> > I'm looking for that short - not too hard to
> > understand - snippet of code that will show them *why*. I'm convinced
> > that such a snippet must involve macros. But all the examples I ran
> > into so far have been either too simple - and could be easily
> > implemented another way (for example with Ruby's blocks), or too
> > complicated.
>
> > Any suggestions for such an example ?
>
> Here is my best attempt so far:http://p-cos.blogspot.com/2007/02/what-is-point-of-macros.html

The problem with this example is that, while it does demonstrate that
macros are structural transforms of the code itself, it doesn't do
anything that can't also be done in languages with light-weight
lambdas. Eg, if you were to look at Smalltalk code written by me,
you'll notice things like:

outfile withOutputStream: [:out |
infile doInputLines:
[:line | out nextLinePut: line]]

withOutputStream abstracting away the need for ensure: (ie, unwind-
protect), and doInputLines: abstracting away that plus the loop of
reading line-by-line while input is available. While abstracting away
the lambdas is a win for Lisp, that's not universal, so at least a
doubtful ST'er won't see anything there.

Rainer Joswig

unread,
Sep 12, 2007, 6:38:29 AM9/12/07
to
In article <1189590672.5...@57g2000hsv.googlegroups.com>,

I also can't see why

(with-input-file (in filename)
(do-something in))

is better than

(call-with-input-file filename do-something)

In the case of a &body we have:

(call-with-input-file filename
(lambda (in)
...))

With a readmacro we could have:

(call-with-input-file filename [in |
...])


But then what with parameters?


(with-input-file (in filename :element-type '(unsigned-byte 0))
(do-something in))

What now with CALL-WITH-INPUT-FILE?

(call-with-input-file filename do-something :element-type '(unsigned-byte 0))

Extensible, more keyword parameters can added to the end. Unfortunately
they are to behind the function parameter

or

(call-with-input-file filename do-something '(:element-type (unsigned-byte 0)))

Take all parameters and convert them into a list. Usually not CL style for
basic function interfaces.

or

(call-with-input-file filename '(:element-type (unsigned-byte 0)) do-something)

As above, but we can move the additional parameters before the function
parameter. Gets easier to read with long code. See below. Still not
CL argument list style.


Then

(call-with-input-file filename
(lambda (in)
...)
:element-type '(unsigned-byte 0))

Makes reading harder when you have larger code. Options should be above
the function in the text.

or

(call-with-input-file filename
(lambda (in)
...)
'(:element-type (unsigned-byte 0)))

or

(call-with-input-file filename
'(:element-type (unsigned-byte 0))
(lambda (in)
...))

To move the options to the front we need to make them a list.
Something like (foo arg :bar1 a1 :bar2 a2 ... :barn an fn) is not a good
arglist in CL.

So, the macro allows you to group the parameters better. New args can be added easily,
the args don't need to be listified and the code body comes last.

(with-input-file (in filename :element-type '(unsigned-byte 0))
(do-something in))

--
http://lispm.dyndns.org

Pascal Costanza

unread,
Sep 12, 2007, 6:39:21 AM9/12/07
to

That's why I said "my best attempt so far", and not "a good attempt." ;)

The two examples in that blog entry crystallize what I personally think
is the essence of macros. I also noticed that many people don't get the
second example though. It'd be nice to see another example that's
equally compact and conveys the same idea.

alkond...@gmail.com

unread,
Sep 12, 2007, 6:46:39 AM9/12/07
to
I think SETF facility is the best example. You can build your own
magic on top of it (like ! macro from "On Lisp"). E.g. some other
languages have concept of reference, but few support generic places.
This is something them I'm missing in other languages (except C++
where it's possible to do similar things with it's templates black
magic).


Eli Bendersky

unread,
Sep 12, 2007, 7:00:58 AM9/12/07
to
On Sep 12, 10:28 am, Pascal Costanza <p...@p-cos.net> wrote:
> Eli Bendersky wrote:
> > Hello all,
>
> > In short: I'm looking for a "killer" macro - a macro (or a couple of
> > related ones) that show what Lisp can do and other languages (those
> > without uniform syntax) can not.
>
> > A longer version: Many contemporary languages boast their sharing most
> > of the functional features of Lisp. Perl, Ruby, Javascript - all have
> > convenient lists (or arrays), functions as first class objects,
> > garbage collection, dynamic typing, lexical closures. However, one
> > thing they lack is uniform syntax, and hence the service of a powerful
> > built-in macro system such as Common Lisp's "defmacro".
> > When confronted by fellow programmers with the question "so why is
> > Lisp so special", I'm looking for that short - not too hard to
> > understand - snippet of code that will show them *why*. I'm convinced
> > that such a snippet must involve macros. But all the examples I ran
> > into so far have been either too simple - and could be easily
> > implemented another way (for example with Ruby's blocks), or too
> > complicated.
>
> > Any suggestions for such an example ?
>
> Here is my best attempt so far:http://p-cos.blogspot.com/2007/02/what-is-point-of-macros.html
>

Pascal, this can be achieved in Ruby in a simpler way, so it won't
cut.

I think that the direction should be towards some brilliant example of
a simple DSL that looks like any other Lisp code. *this* can't be done
in a non-uniform syntax language.

Eli


Nicolas Neuss

unread,
Sep 12, 2007, 7:11:02 AM9/12/07
to
Christophe <christophe...@birdtechnology.net> writes:

Seconded. The point is that a CL programmer can be pretty sure that he can
adapt CL to any new programming paradigm without hacking the CL
implementation, whereas in other languages this is often impossible. Tell
them to implement a full CLOS-equivalent (dynamic class changes, multiple
inheritance, multimethods) in their respective language. Then tell them
that this was possible in pre-CL-Lisp.

Nicolas

Tim Bradshaw

unread,
Sep 12, 2007, 7:47:04 AM9/12/07
to
On Sep 12, 12:00 pm, Eli Bendersky <eli...@gmail.com> wrote:

> I think that the direction should be towards some brilliant example of
> a simple DSL that looks like any other Lisp code. *this* can't be done
> in a non-uniform syntax language.

The canonical DSL that looks like any other Lisp code is HTML

Alex Mizrahi

unread,
Sep 12, 2007, 7:52:41 AM9/12/07
to
(message (Hello 'Eli)
(you :wrote :on '(Wed, 12 Sep 2007 06:59:19 -0000))
(

EB> understand - snippet of code that will show them *why*. I'm convinced
EB> that such a snippet must involve macros. But all the examples I ran
EB> into so far have been either too simple - and could be easily
EB> implemented another way (for example with Ruby's blocks), or too
EB> complicated.

EB> Any suggestions for such an example ?

well, best of my personal achievements so far is a macro for RDF queries,
that automatically creates kinda SPARQL query, binding lexical variables
wherever needed, executes it and binds result to lexical variables. i've
shown it to some haskellist once, and he had to admit that such DSL in
Haskell will either have freaky syntax/semantics, or that would be external
DSL.
as a bonus of using embedded DSL i can use it togerther with HTML generation
macros:

(:p
(:table
(:tr (:th "username") (:th "department") (:th "salary"))
(rdf-do-query
((?user :username ?uname)
(?user :salary ?salary)
(?user :department ?dept)
(?dept :name ?dept-name))
(html
(:tr
(:td (:princ ?uname))
(:td (:princ ?dept-name))
(:td (:princ ?salary)))))))

if you'd like some more details i can provide them.
afair there were some libraries that can express SQL in a lispy notation and
well integrated -- something called Uncommon SQL or whatever..pretty
impressive too, IMO.

)
(With-best-regards '(Alex Mizrahi) :aka 'killer_storm)
"i've killed myself i've been born again")


Pascal Costanza

unread,
Sep 12, 2007, 8:03:43 AM9/12/07
to
Eli Bendersky wrote:
> On Sep 12, 10:28 am, Pascal Costanza <p...@p-cos.net> wrote:
>> Eli Bendersky wrote:
>>> Hello all,
>>> In short: I'm looking for a "killer" macro - a macro (or a couple of
>>> related ones) that show what Lisp can do and other languages (those
>>> without uniform syntax) can not.
>>> A longer version: Many contemporary languages boast their sharing most
>>> of the functional features of Lisp. Perl, Ruby, Javascript - all have
>>> convenient lists (or arrays), functions as first class objects,
>>> garbage collection, dynamic typing, lexical closures. However, one
>>> thing they lack is uniform syntax, and hence the service of a powerful
>>> built-in macro system such as Common Lisp's "defmacro".
>>> When confronted by fellow programmers with the question "so why is
>>> Lisp so special", I'm looking for that short - not too hard to
>>> understand - snippet of code that will show them *why*. I'm convinced
>>> that such a snippet must involve macros. But all the examples I ran
>>> into so far have been either too simple - and could be easily
>>> implemented another way (for example with Ruby's blocks), or too
>>> complicated.
>>> Any suggestions for such an example ?
>> Here is my best attempt so far:http://p-cos.blogspot.com/2007/02/what-is-point-of-macros.html
>
> Pascal, this can be achieved in Ruby in a simpler way, so it won't
> cut.

No, you can't do this in Ruby at all.

It's important to understand why that's the case.

I agree that it's easy to miss the difference, so maybe it's not a good
illustration for that reason. But the difference is definitely there.

Pascal Costanza

unread,
Sep 12, 2007, 8:40:54 AM9/12/07
to

Same problem as with my while macro - or probably even worse. Many
people don't see why (setf (car cons) 345) is better than (set-car! cons
345).

I think my while macro is more convincing because it hides away an
aspect of the implementation (the fact that lambdas are used in a
functional version) that you cannot hide away otherwise.

With setf nothing is hidden away. It's "just" nicer.

Eli Bendersky

unread,
Sep 12, 2007, 8:56:38 AM9/12/07
to

What, specifically, can't you do ? with-open-file ? while ?


Pascal Costanza

unread,
Sep 12, 2007, 9:04:32 AM9/12/07
to

While without lambdas (or blocks, or whatever you want to call them).

Zach Beane

unread,
Sep 12, 2007, 9:10:04 AM9/12/07
to
Eli Bendersky <eli...@gmail.com> writes:

> I think that the direction should be towards some brilliant example of
> a simple DSL that looks like any other Lisp code. *this* can't be done
> in a non-uniform syntax language.

Here are a couple of my favorite examples:

- http://groups.google.com/group/comp.lang.lisp/msg/2ff89986ee77f639

- http://groups.google.com/group/comp.lang.lisp/msg/86cf454beb8a42f9

Zach

Andy Chambers

unread,
Sep 12, 2007, 9:44:01 AM9/12/07
to

I think that html "templating" might be a contender. There's probably
nothing lisp can do that nothing else can if the "else" is allowed
unlimited code but I bet cl-who (or aserve's offering) uses a lot less
code than any of the php/python/ruby template engine's and the code
that uses it looks better too.

--
Andy

Kent M Pitman

unread,
Sep 12, 2007, 9:50:31 AM9/12/07
to
Christophe <christophe...@birdtechnology.net> writes:

> No needs to search a long time : loop, iterate or defclass are smart
> examples.

(And the error handling operations offer additional examples.)

Interesting that you should choose these particular choices to suggest,
because...

During ANSI CL design, the process was broken into these major
interpersonal subgroups, taking on partial design:

- iteration [yielded LOOP]
- object system [yielded CLOS (DEFCLASS, DEFMETHOD, etc.)]
- errors [yielded the condition system (HANDLER/RESTART-BIND/CASE,
IGNORE-ERRORS, etc.)]
- pretty printer [yielded many functions, but also some macros like
PPRINT-POP]

and then there was a mass of small cleanups done by the compiler and
cleanup groups. But my point is that the large shifts in language
were prototyped by subgroups, and generally delivered intact with
working code that included language changes largely implementable by
users. (The condition system design, which I headed up, for example,
had a sample implementation you can grab from
http://www.nhplace.com/kent/CL/
It was written for an older dialect of Common Lisp [CLTL] and had no
way to hook into the system, so required minor support in that regard,
but basically was enough to give all implementors a sense of what was
being asked for without having to modify their systems.)

This ability for users to lead the development of extensions and then
propose them to the implementors, rather than to demand access to the
internals of all implementations (including proprietary ones) is very
empowering. It's very easy to say "well, everything should be open
source and I then I could propose proper internal modifications" but
ignoring the political/business issues there and assuming they could
be magically glossed, there is still the fact that those
implementations vary widely and that's a lot to ask of an
extension--that you provide source code to disparate implementations.
You can also say that argues for one implementation, but then that
says that people who wanted smaller implementations or ones with
different performance characteristics would be out of luck.)

Macros offer the opportunity for users to contribute to language
design AND to work around the fact that at least some contributions
are likely to be rejected, since they can still use their own
extensions and can share them with others either to gain more support
or just to make a secret club of outcasts that live happily among each
other. And, incidentally, lest you think this makes a two-tier system,
in fact I think the motivation of some (perhaps many) of the designers
was that they knew THEY were not getting what they wanted (as
individuals) and were slipping in flexibilities to allow them to be
able to live happily with others' unwillingness to compromise.

philip....@gmail.com

unread,
Sep 12, 2007, 9:59:08 AM9/12/07
to
On Sep 12, 9:28 am, Pascal Costanza <p...@p-cos.net> wrote:
> Eli Bendersky wrote:
> > In short: I'm looking for a "killer" macro - a macro (or a
> > Any suggestions for such an example ?
>
> Here is my best attempt so far:http://p-cos.blogspot.com/2007/02/what-is-point-of-macros.html

Over the last few months I've shown Pascal's example to a few of my
skeptical colleagues and have had a reasonable success rate (no actual
converts but certainly some curiosity). I think the examples are good
because they're short and in my experience, when advocating a
programming language, you have about 60 seconds to hook someone before
they return to Eclipse.

I suspect these examples work best on Java and C# programmers as these
languages provide the least support for this way of thinking. The more
dynamic languages all have their own approximations which make the
examples seem less impressive. But perhaps Ruby, Python and Smalltalk
programmers can better appreciate a longer DSL-style example.

--
Phil
http://phil.nullable.eu/

Kent M Pitman

unread,
Sep 12, 2007, 10:02:35 AM9/12/07
to
Rainer Joswig <jos...@lisp.de> writes:

> I also can't see why
>
> (with-input-file (in filename)
> (do-something in))
>
> is better than
>
> (call-with-input-file filename do-something)

1. Because of indentation issues. The former makes more textually
appropriate use of visual space.

2. Because of aesthetic issues that amount to a religious divide and that
may or may not appeal to you, but that are real to many. In the history
of mathematics, there was a longstanding confusion about the difference
between a function and an expression, owing in part to notations like

/
| x^2 dx
/

or if you prefer Lisp notation:

(integral (^ x 2) x)

rather than:

int(f) where { f(x) = x^2 }

There are some people who think that the change was a big improvement
and others who find it cumbersome to think of integrals this way. See
my 1980 paper on macros
( http://www.nhplace.com/kent/Papers/Special-Forms.html )
for the issues surrounding why people write macros and yet why it's useful
to be able to get at the functional form. I think both are important.

I used to program in a world, around 1978 when I arrived to Lisp, where
LET was a macro users had to write and we wrote everything as
((lambda (x) ...) binding-for-x)
The problem was that it made things execute backwards [first the binding
then the body, which was textually to the left or above]. Macros give
you control of that, too (especially important if your language is not
lazy).

3. A logical extension of your argument is to suggest that IF has no purpose
as a macro, and people should write a function:
(*if test (lambda () consequent) (lambda () alternative))
If you don't see that some of this is just syntactic baggage, you won't
get this argument. Making a lambda just to consume it is like the user
doing the system's job. And that's how people feel about
(call-with-input-file filename do-something)
when the do-something is
(call-with-input-file filename (lambda (stream) ...))
Every word there has a purpose except the lambda, which is just going to
get opened back into a function body instantly, and which in the meantime
is only further increasing indentation, and doing so EVEN IF you move it
to a new line, since it costs you three lines to get even close to where
the other would have done
(call-with-input-file filename
(lambda (stream)
...))
And even then if you want to give "options" you can't see them at the start
where you are going to need to if reading top-to-bottom/left-to-right,
you'll see them at the end, out of order from the evaluation order.

I am NOT trying to say that you should believe these arguments, but can you
see how some of these things would matter to others?

Rainer Joswig

unread,
Sep 12, 2007, 10:29:00 AM9/12/07
to
In article <u642g2...@nhplace.com>,

Kent M Pitman <pit...@nhplace.com> wrote:

> Rainer Joswig <jos...@lisp.de> writes:
>
> > I also can't see why
> >
> > (with-input-file (in filename)
> > (do-something in))
> >
> > is better than
> >
> > (call-with-input-file filename do-something)
>
> 1. Because of indentation issues. The former makes more textually
> appropriate use of visual space.

But that could be fixed with a indentation rule.

>
> 2. Because of aesthetic issues that amount to a religious divide and that
> may or may not appeal to you, but that are real to many. In the history
> of mathematics, there was a longstanding confusion about the difference
> between a function and an expression, owing in part to notations like

I agree with 2.

I also think that not everything should be expressed in functional terms
and also that not every macro should expand into a functional expression.
If WHILE has a more primitive expansion, I'm happy with it.

> 3. A logical extension of your argument is to suggest that IF has no purpose
> as a macro, and people should write a function:
> (*if test (lambda () consequent) (lambda () alternative))
> If you don't see that some of this is just syntactic baggage, you won't
> get this argument. Making a lambda just to consume it is like the user
> doing the system's job. And that's how people feel about
> (call-with-input-file filename do-something)
> when the do-something is
> (call-with-input-file filename (lambda (stream) ...))
> Every word there has a purpose except the lambda, which is just going to
> get opened back into a function body instantly, and which in the meantime
> is only further increasing indentation, and doing so EVEN IF you move it
> to a new line, since it costs you three lines to get even close to where
> the other would have done
> (call-with-input-file filename
> (lambda (stream)
> ...))
> And even then if you want to give "options" you can't see them at the start
> where you are going to need to if reading top-to-bottom/left-to-right,
> you'll see them at the end, out of order from the evaluation order.

Somehow I wouldn't put things like IF, WHILE, LOOP, CATCH etc.
into the same category as CALL-WITH-OPEN-FILE. I see the latter
as a higher-order'task'. One I also want to see on the stack. Directly. With
useful parameters. I could for example extract the DO-SOMETHING
function in a debugger and apply it to something. WITH-OPEN-FILE
is invisible on the stack. It creates some weird code that
has no simple representation. Worse. it might be completely replaced
with a bunch of new things I would never associate with WITH-OPEN-FILE.
When I want to edit source for the stack frame, it somehow has to figure
out that there was some macro call and find it. WITH-OPEN-FILE also can't be composed
with other functions. I feel it is just in the way. In the
source code it might look useful. But the one-2-one correspondence
of source and what I see on the stack is gone. I'm willing to
give that up for IF, WHILE, LOOP and others - but somehow not
for CALL-WITH-OPEN-FILE.

>
> I am NOT trying to say that you should believe these arguments, but can you
> see how some of these things would matter to others?

Sure.

--
http://lispm.dyndns.org

Ken Tilton

unread,
Sep 12, 2007, 10:34:47 AM9/12/07
to

Eli Bendersky wrote:
> Hello all,
>
> In short: I'm looking for a "killer" macro - a macro (or a couple of
> related ones) that show what Lisp can do and other languages (those
> without uniform syntax) can not.

Search c.l.lisp,c.l.python a coupla years back for a thread on why
Python should macros in which I offer DEFGOAL (or something like that)
from my RoboCup effort, called variously RoboCells and TeamKenny.

kt

--
http://www.theoryyalgebra.com/

"We are what we pretend to be." -Kurt Vonnegut

Ken Tilton

unread,
Sep 12, 2007, 10:41:32 AM9/12/07
to

Christophe wrote:
> On 12 sep, 08:59, Eli Bendersky <eli...@gmail.com> wrote:
>
>>Hello all,
>>
>>In short: I'm looking for a "killer" macro - a macro (or a couple of
>>related ones) that show what Lisp can do and other languages (those
>>without uniform syntax) can not.

....

> No needs to search a long time : loop, iterate or defclass are smart
> examples.

You are absolutely right, but others will say, "Hunh? Those are part of
the language. You think implementing classes in C++ is easy?" They won't
get that the CLOS implementor is not doing amazing things in the
compiler (at this point) rather merely using a tool (macros) available
to application programmers as well.

What is needed is an example of where an application code pattern can be
automated, and natch it has to be one where a function would not do.

Brian Adkins

unread,
Sep 12, 2007, 11:54:22 AM9/12/07
to
On Sep 12, 9:04 am, Pascal Costanza <p...@p-cos.net> wrote:
> Eli Bendersky wrote:
> > What, specifically, can't you do ? with-open-file ? while ?
>
> While without lambdas (or blocks, or whatever you want to call them).
>
> Pascal

I think Eli was focused on the first example in the article (possibly
because it was the first example) which is easy to do in Ruby with a
block:

File.open(filename) {|in| do_something(in) }

I think you're right about not being able to do while w/o lambdas, but
it's not too bad (syntactically) with them. The efficiency is worse
than macros, but then Ruby is dog slow anyway:

def while_function pred, &b
if pred.call
yield
while_function(pred, &b)
end
end

n = 3
while_function(lambda { n > 0 }) do
puts n
n -=1
end

Having notational convenience via macros plus efficiency is a big win
with Lisp. From my perspective as a Ruby programmer learning Lisp, the
thought of having a more powerful language *and* order(s) of magnitude
faster execution felt like having my cake and eating it too.

Alan Crowe

unread,
Sep 12, 2007, 1:54:43 PM9/12/07
to
Eli Bendersky <eli...@gmail.com> writes:

> In short: I'm looking for a "killer" macro - a macro (or a couple of
> related ones) that show what Lisp can do and other languages (those
> without uniform syntax) can not.
>

> Any suggestions for such an example ?
>

Many computer programming languages have the concept of an
enumeration or symbolic constant. At compile time the
constant is a string of characters, meaningful to humans,
but comparision is character by character, so long names are
slower than short names. At run time this name has gone
away, leaving a number fitting in a machine word. So items
from small sets are represented efficiently, as a code
number in a machine word, while the programmer does not have
to learn the code numbers. He programs with symbolic names.

Lisp has a better idea - the translation from names to
numbers takes place at read time, and the number is the
address of the symbol. We get the efficiency of representing
the constant as a single word, but the name is still
available at run-time.

However, sometimes the coding scheme for these code numbers
is not internal to the program. Sometimes the numbers come
from outside, as for example in the instruction set of a
microprocessor. The first idea here is something like

(defconstant load 8)
(defconstant store 9)

but CL:CASE stops working.

(case mnemonic
(load ....)
(store ...)

has to be replace by

(case mnemonic
(8 ...)
(9 ...)

which is unacceptable.

A better idea is to stick with symbols and write assemby and
disassembly functions, so that

(motorola6800 'load) => 10
(intel8086 'load) => 32

(motorola6800 10) => load
(intel8086 10) => decrement

or whatever, I don't know the actual code.

Well, that is a potentially better idea. You don't what to
be stuck for hours typing these things. You want want to be
type in something simple, such as

(define-opcodes mc6809
(8 load store
16 add sub mul div
24 fetch write))

On the other hand, they might get heavily used, the program
had better scale well. The target is for constant time
translation, regardless of the number of opcodes.

So one design is to have a vector of symbols, with each
symbol in the right place to be looked up directly, and to
use the machine name as an indicator on each symbols plist,
giving the opcode behind that mnemonic on that architecture.

At this point I just paste in the file from alan/lisp/utils

#| the built in case macro doesn't evaluate the keys

This causes grief when you want symbols to work like C-style
#define LOAD 0
#define STORE 3
#define ADD 4
#define SUBTRACT 5

(defmacro define-opcodes ... )

(define-opcodes octium (0 load 3 store add subtract))

and then you have

(octium 'store) => 3
(octium 4) => add

You work with symbols and case works fine.
The bonus is that you can define different encodings and
parameterise your assembler and dis-assembler

(funcall #'machine opcode) => mnemonic

|#

(defmacro define-opcodes (machine condensed-list)
"number mnemonic mnemnonic number ...."
(let* ((code-assoc (expand condensed-list))
(least-opcode (reduce #'min code-assoc :key #'cdr)))
`(let ((opcode-vector ,(make-decode-table code-assoc least-opcode))
(least-opcode ,least-opcode))
(defun ,machine (thing)
(etypecase thing
(number (aref opcode-vector (- thing least-opcode)))
(symbol (get thing ',machine))))
,(cons 'setf
(loop for (mnemonic . code) in code-assoc
append
`((get ',mnemonic ',machine) ,code)))
',machine)))

(defun expand (list)
(let ((expansion '())
(index 0))
(dolist (item list expansion)
(etypecase item
(symbol (push (cons item index) expansion)
(incf index))
((integer 0) (setf index item))))))

(defun make-decode-table (code-assoc least-opcode)
(let ((greatest-opcode (reduce #'max
code-assoc
:key #'cdr)))
(let ((table (make-array (+ 1 (- greatest-opcode
least-opcode)))))
(loop for (mnemonic . code) in code-assoc
do (setf (aref table (- code least-opcode))
mnemonic))
table)))

Quite a short file.

(macroexpand-1 '(define-opcodes mc6809
(8 load store
16 add sub mul div
24 fetch write)))
=>
(LET ((OPCODE-VECTOR
#(LOAD STORE 0 0 0 0 0 0 ADD SUB MUL DIV 0 0 0 0 FETCH WRITE))
(LEAST-OPCODE 8))
(DEFUN MC6809 (THING)
(ETYPECASE THING
(NUMBER (AREF OPCODE-VECTOR (- THING LEAST-OPCODE)))
(SYMBOL (GET THING 'MC6809))))
(SETF (GET 'WRITE 'MC6809)
25
(GET 'FETCH 'MC6809)
24
(GET 'DIV 'MC6809)
19
(GET 'MUL 'MC6809)
18
(GET 'SUB 'MC6809)
17
(GET 'ADD 'MC6809)
16
(GET 'STORE 'MC6809)
9
(GET 'LOAD 'MC6809)
8)
'MC6809)
T

I've not used this for real. If I were going to I would add
more error checking (and some eval-when), which would make
it more complicated and less useful as a simple example.

I think it serves to bring out the idea of writing code, and
not just pasting code into a template, while being
unsophisticated enough to explain to non-lispers.

Alan Crowe
Edinburgh
Scotland

Emilio Lopes

unread,
Sep 12, 2007, 2:33:57 PM9/12/07
to
Eli Bendersky writes:

> In short: I'm looking for a "killer" macro - a macro (or a couple of
> related ones) that show what Lisp can do and other languages (those
> without uniform syntax) can not.

Here is my take: a simple macro called `debug', which prints a given
*expression* followed by its value. As a convenience it also returns
this same value.

I'd be interested to know how you could achieve this in languages
missing macros and `quote'.

This answer uses Scheme, but porting to CL's `defmacro' should be
simple.

Supose you define `my-function' as follows:

(define (my-function x y z)
(* 2 (debug (+ x y z))))

If you call `(my-function 3 4 2)' it will now print

### (+ x y z): 9

and return 18.

Here is the macro definition:

(define-syntax debug
(syntax-rules ()
((_ expr)
(let ((literal (quote expr))
(value expr))
(display "### ")
(display literal)
(display ": ")
(write value)
(newline)
value))
((_ expr expr1 ...)
(begin
(debug expr)
(debug expr1 ...)))))

See also

http://programming.reddit.com/info/ujj3/comments/cuqof

where someone asked this same question.

--
Emílio C. Lopes
Munich, Germany

Thomas A. Russ

unread,
Sep 12, 2007, 2:21:50 PM9/12/07
to
Eli Bendersky <eli...@gmail.com> writes:

> Hello all,


>
> In short: I'm looking for a "killer" macro - a macro (or a couple of
> related ones) that show what Lisp can do and other languages (those
> without uniform syntax) can not.

Well, one nice, small example that I like is:

WITH-OPEN-FILE

which actually doesn't really depend all that much on uniform syntax,
but more on just being able to wrap arbitrary code around something
else. This illustrates the ability to build structures to do clean-up.
Now, some C++ aficionados will point to destructors as a way to do
that. The reply there is to ask how one would have added a feature like
that to the language as a programmer? In lisp you can do it.

If you want to do something more web-ish, then perhaps a simple HTML
generation example would do. It also can illustrate some of the
processing features being able to program the macro can do. Something
like the following (omitting the ONCE-ONLY or GENSYM treatment for
clarity):

(defmacro with-html-tag ((stream tag &rest attribute-value-pairs)
&body body)
`(progn
(format ,stream "<~a~{ ~a='~a'~}" ,tag ,attribute-value-pairs)
,(if (null body)
`(format ,stream "/>")
`(format ,stream ">"))
,@body
,@(when body `((format ,stream "</~a>" tag)))))

(with-html-tag (web-response-stream "H1")
(compute-and-print-title-for-this-page web-response-stream))

(with-html-tag (*standard-output* "FONT" "COLOR" "#FF0000")
(with-html-tag (*standard-output* "B")
(with-html-tag (*standard-output "I")
(write-string "This is great!"))))

--
Thomas A. Russ, USC/Information Sciences Institute

Raymond Wiker

unread,
Sep 12, 2007, 3:34:24 PM9/12/07
to
Emilio Lopes <ec...@gmx.net> writes:

Not a convincing example of the power of (Lisp) macros; even C++ can do this:

#include <iostream>
#include <cmath>

#define DEBUG(a) (std::cerr << "### " << #a << ": " << (a) << std::endl, a)

main()
{
DEBUG(1 + 1);
DEBUG(asin(1.0));

float a = DEBUG(1 + 1) + DEBUG(asin(1.0)) + 3.14;

std::cerr << a << std::endl;
}

Thomas F. Burdick

unread,
Sep 12, 2007, 5:45:31 PM9/12/07
to

Yeah, I got that :-)
It is a difficult problem.

> The two examples in that blog entry crystallize what I personally think
> is the essence of macros. I also noticed that many people don't get the
> second example though. It'd be nice to see another example that's
> equally compact and conveys the same idea.

The big problem I see with those examples is that what they're trying
to use structural transformation as a lever to do is ... hide
lambdas. I'm not really au courant of the most recent scripting
languages, but I have the impression that they're on an asymptotic
course not so much with Lisp as with Smalltalk. So aiming one's
arguments at ST'ers might be overshooting, but that's okay.

For the problem of wanting to write your own "do while except return
when" control structure (to pick a silly example), there's a perfectly
good non-macro solution. In ST, not only are anonymous functions
hilariously light-weight from a syntactic point of view, but the
*entire* language uses a dead-simple evaluation model: the receiving
expression is evaluated, then the arguments to pass with the message,
then the message is passed. Always, that's it. The language itself
doesn't really have special forms, they're all implemented as methods
-- which means that *all* divergences from the normal order of
evaluation use blocks, whether they be the super-banal ifTrue:ifFalse:
or my wacky doWhileTrue:exceptReturnWhen: [1]. This creates a
language ecology where a while/if/do-while-true-except-return-when
that works in a functional style does not in fact leak abstractions.
"This changes the order of evaluation" is part of the interface, not
the implementation.

Of course Lisp has special forms (like most languages). In this
context, saying that one of the things the uniform syntax buys is
macros, and one of the uses of macros is to hide lambdas (which are
clumsy *not* because lambda is spelled l-a-m-b-d-a, but because the
built-in special forms work by magic, not via lambdas), therefore
macros let us extend the language in a seamless manner ... well, as
true as that is in the design ecology of the Lisp languages, it's very
much an argument trying to stand on its own shoulders from the point
of view of ST. That's the sympathetic point of view -- it could also
be seen as an ex-post-facto justification of an ugly hack.

So, accepting that there are other combinations of language features
that take care of the lambda issue, what do macros buy you? Not the
ability to add control structures that aren't primitively there (lack
of unwind-protect is not something you can solve with macros, no
matter how much I wish that it were so). Not the ability to write
seamless DSL's (see Avi Bryant's Roe library for a lovely example of
how to incorporate relational algebra into the Smalltalk language).
No, I think the convincing examples will involve really using the
source forms as one of the macro parameters.

Speaking of which, that's actually a whole class of fairly simple,
more-or-less common macros. with-slots and with-accessors are two
obvious examples. Another one which pops to mind is:

;; collect strings describing all the entries in the songs table
;; in some SQL database
(do-query ((name artist album) "songs"
:where "genre = jazz and year > 1956")
(push (format nil "~S by ~A on the album ~S" name artist album)
songs))

The key thread here being that (a) variables are being bound based on
domain-specific information, and (b) one bit of that information is
the name of the variable itself.


[1] Just in case it's not obvious, a ST one would look like:

[ x put i.
i := i + 1
]
doWhileTrue: [ i < 10000 ]
exceptReturnWhen: [ i = z ]

where Lisp would be either:

(do-while-except
(progn (my-push i x)
(incf i))
(< i 10000)
:return-when (eql i z))

or:

(do-while-except-return-when
(lambda () (my-push i x)
(incf i))
(lambda () (< i 10000))
(lambda () (eql i z)))


Thomas A. Russ

unread,
Sep 12, 2007, 4:25:39 PM9/12/07
to
Brian Adkins <lojic...@gmail.com> writes:

> On Sep 12, 9:04 am, Pascal Costanza <p...@p-cos.net> wrote:
> > Eli Bendersky wrote:
> > > What, specifically, can't you do ? with-open-file ? while ?
> >
> > While without lambdas (or blocks, or whatever you want to call them).
> >
> > Pascal
>
> I think Eli was focused on the first example in the article (possibly
> because it was the first example) which is easy to do in Ruby with a
> block:
>
> File.open(filename) {|in| do_something(in) }
>
> I think you're right about not being able to do while w/o lambdas, but
> it's not too bad (syntactically) with them. The efficiency is worse
> than macros, but then Ruby is dog slow anyway:

Well, the point here would be: How could you implement that in Ruby IF
the language didn't already have it in the first place? In other words,
could you add something that works like that syntactically if the
compiler writers and language designers HAD NOT ALREADY built it for
you?

That's the beauty of Lisp macros. You can add them.

Loop is an example of an iteration construct that was (quite literally)
added to the lisp language through macro programming. And for those who
don't like loop, there's series. How many languages would let you
create a WHILE or a FOR loop construct when the language didn't already
support it?

Rayiner Hashem

unread,
Sep 12, 2007, 6:02:39 PM9/12/07
to
> What is needed is an example of where an application code pattern can be
> automated, and natch it has to be one where a function would not do.
>
> kt

Alexanderscu's "Modern C++ Design" is basically exactly about this:
implementing "patterns" (in the Gang of Four sense) in code. Except he
uses a weak "macro" language to express these solutions. You could go
through the book and pick out few and show how they are much easier to
express with Lisp macros.

Dimiter "malkia" Stanev

unread,
Sep 12, 2007, 6:08:13 PM9/12/07
to
For a code that was doing LZO decompression, I've added the following
macros, that were "simulating" how for example "*p++ = value", or "*p++
= *x++", or memcpy(). Granted not the best macros, but minimized my code.

(defmacro aref++ (array index)
"Does the same job aref does, but increases index after that"
`(aref ,array (prog1 ,index (incf ,index))))

(defmacro acpy++1 (dst dst-index src src-index)
`(setf (aref++ ,dst ,dst-index)
(aref++ ,src ,src-index)))

(defmacro acpy++ (dst dst-index src src-index times)
(if (constantp times)
(let ((code))
(dotimes (ignored times)
(push `(acpy++1 ,dst ,dst-index ,src ,src-index) code))
(push 'progn code)
code)
`(dotimes (ignored ,times)
(acpy++1 ,dst ,dst-index ,src ,src-index))))

Here is an example of how to use them

The following would copy 15 bytes from the source-array starting from
the source-index, in the dest-array, starting from the dest-index. It
would then increase dest-index and source-index by 15.

(acpy++ dest-array dest-index source-array source-index 15)

Also note that this would check whether the number of indexes is
constant, and in the case above it is (15), so it would actually unroll
that, instead of loop it.

Thanks,
Dimiter "malkia" Stanev.


Kent M Pitman

unread,
Sep 12, 2007, 8:18:32 PM9/12/07
to
Rainer Joswig <jos...@lisp.de> writes:

> In article <u642g2...@nhplace.com>,
> Kent M Pitman <pit...@nhplace.com> wrote:
>
> > Rainer Joswig <jos...@lisp.de> writes:
> >
> > > I also can't see why
> > >
> > > (with-input-file (in filename)
> > > (do-something in))
> > >
> > > is better than
> > >
> > > (call-with-input-file filename do-something)
> >
> > 1. Because of indentation issues. The former makes more textually
> > appropriate use of visual space.
>
> But that could be fixed with a indentation rule.

Ick. I hate for functions to be indented like macros. I use the presence
of non-standard indentation to alert me to the non-standard evaluation rules.

So count that as "no" for me, even though I perfectly well understand
that you might want to take such an approach. As I said, and as I'm
pretty sure you correctly understand already, the intent of my remarks
wasn't to "convince you", it was to "explain me".

I admit to being surprised that you find this indentation fix aesthetic,
since I only ever do such things in outright desperation for screen space,
but I'm glad you mentioned it and I accept that you might differ on this.

> > 3. A logical extension of your argument is to suggest that IF has no purpose
> > as a macro, and people should write a function:
> > (*if test (lambda () consequent) (lambda () alternative))
>

> Somehow I wouldn't put things like IF, WHILE, LOOP, CATCH etc.
> into the same category as CALL-WITH-OPEN-FILE. I see the latter
> as a higher-order'task'.

Well, in the context of CL, the underlying function is OPEN, and it's
already available as a function. You're right that this could be done
as a function, but historically it did not arise that way.

Before WITH-OPEN-FILE, I used to just use a LET with an OPEN, and I
remember someone getting really mad at me one day on the PDP-10
running the ITS operating system, which had only a finite number of
file channels. I'd used them all up, not realizing it, because the
bindings were gone but the GC had not run (which would have closed
them). I asked what to do and no one had a good answer. Soon after,
there was an UNWIND-PROTECT added to the language, and I wrote a macro
called IOTA (trying to make it greek so it would sound like LAMBDA, a
binding form, but also like "I/O"). It allowed multiple binding forms,
so it was easy to rewrite a LET into it. I think that's how we
started down the path to having it be a macro. IOTA became widely
used in MACLISP, at least at the MIT Lab for Computer Science--I don't
know if it got distributed. We weren't very formal about these things.
[On another day, ask me about PI, the macro I wrote for binding...
you guessed it... program interrupts. It was later renamed to
WITHOUT-INTERRUPTS.]

Incidentally, too, back to CL, you should know that when I write these
WITH-xxx macros, I almost always accompany them with a CALL-WITH-xxx
so that I can do either kind of call. But I find I almost never use
the CALL-WITH-xxx even when I was the one to provide it as an option.
The main reason I write them is not to use them but to make
redefinition easier, since I can redefine the CALL-WITH-xxx without
redefining the macro, and so I don't have to recompile the callers if
the definition changes.

> One I also want to see on the stack. Directly. With
> useful parameters. I could for example extract the DO-SOMETHING
> function in a debugger and apply it to something. WITH-OPEN-FILE
> is invisible on the stack.

Well, not the way I generally do it, so I mostly don't see this deficiency.
(Note: I didn't say "I don't see this AS a deficiency", I mean it really
isn't one because we (you and I) both see the same thing on the stack,
since my macro call often expands into the function call that you wrote
explicitly, and so at the time of looking on the stack, the textual pedigree
of the call is long-since erased and the two techniques are blurred by
a common functional implementation.)

Put another way, I often start with the implementation of the function you like
and THEN write the macro because I hate its syntax. You seem to be assuming
I'm starting with a blank sheet and first writing a macro. But in my case,
at least, that's often not true. It probably happens sometimes, but more
and more, it's rare, and I have good [private] macrology for managing this.

> It creates some weird code that
> has no simple representation. Worse. it might be completely replaced
> with a bunch of new things I would never associate with WITH-OPEN-FILE.
> When I want to edit source for the stack frame, it somehow has to figure
> out that there was some macro call and find it.

I just edit the thing on the stack frame and it takes me to the function that
is textually adjacent to the macro.

> WITH-OPEN-FILE also can't be composed
> with other functions.

I use my own CALL-WITH-OPEN-FILE if this is ever needed. (I'm assuming
we're talking about the general case of WITH-xxx macros, not specifically
WITH-OPEN-FILE. If you're meaning to single out the one system function,
then I have no answer other than if I'd ever cared, I'd have written
CALL-WITH-OPEN-FILE as a function that used WITH-OPEN-FILE inside of it,
but I never have had the need. To me, the need is theoretical, not
practical.)

> I feel it is just in the way. In the
> source code it might look useful. But the one-2-one correspondence

(Heh. one-to-one, I assume you mean. Flanked by numbers, I guess
the mind's temptation to substitute a digit is strong there.)

> of source and what I see on the stack is gone.

You could define a CALL-WITH-xxx wrapper. It's not trivial to do if
you want access to system-defined keyword arguments, but it could be
done with a little work, and probably even portably.

> I'm willing to
> give that up for IF, WHILE, LOOP and others - but somehow not
> for CALL-WITH-OPEN-FILE.

Heh. Isn't the brain a marvelous thing? :) It just goes to underscore
my argument that the right simplicity metric for a language is not
"textual minimality" but rather "structural isomorphism to how people
think" (even knowing that this varies person to person, and that it's
hard to figure out precisely ... but it helps explain why a Lisp2 is a
rational concept). I don't doubt you have just such an oddly placed
line in your head--I'm sure I have such a thing in a different place.
It's natural and normal.

> > I am NOT trying to say that you should believe these arguments, but can you
> > see how some of these things would matter to others?
>
> Sure.

Phew. :)

Barry Margolin

unread,
Sep 12, 2007, 9:26:20 PM9/12/07
to
In article <zWSFi.1562$Z%7.1...@newsfe12.lga>,
Ken Tilton <kenny...@optonline.net> wrote:

> Christophe wrote:
> > On 12 sep, 08:59, Eli Bendersky <eli...@gmail.com> wrote:
> >
> >>Hello all,
> >>
> >>In short: I'm looking for a "killer" macro - a macro (or a couple of
> >>related ones) that show what Lisp can do and other languages (those
> >>without uniform syntax) can not.
>
> ....
>
> > No needs to search a long time : loop, iterate or defclass are smart
> > examples.
>
> You are absolutely right, but others will say, "Hunh? Those are part of
> the language. You think implementing classes in C++ is easy?"

LOOP was not originally part of the language, it was a user-written
macro in MacLISP. And even though it's now part of the standard, it's
still implemented pretty much the same way (I'll bet that at least 75%
of the code in most LOOP implementations is from the original MIT LOOP).

And ITERATE is not a part of the language.

--
Barry Margolin, bar...@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***

Barry Margolin

unread,
Sep 12, 2007, 9:33:20 PM9/12/07
to
In article <5kq52mF...@mid.individual.net>,
Pascal Costanza <p...@p-cos.net> wrote:

> alkond...@gmail.com wrote:
> > I think SETF facility is the best example. You can build your own
> > magic on top of it (like ! macro from "On Lisp"). E.g. some other
> > languages have concept of reference, but few support generic places.
> > This is something them I'm missing in other languages (except C++
> > where it's possible to do similar things with it's templates black
> > magic).
>
> Same problem as with my while macro - or probably even worse. Many
> people don't see why (setf (car cons) 345) is better than (set-car! cons
> 345).

Yet they probably have little problem understanding why conventional
languages use the same assignment syntax for everything:

var = val;
array[subscript] = val;
struct.member = val;

They would presumably consider it cumbersome to have to write:

array_assign(array, subscript, val);

Rob Warnock

unread,
Sep 12, 2007, 9:36:24 PM9/12/07
to
Ken Tilton <kent...@gmail.com> wrote:
+---------------

| What is needed is an example of where an application
| code pattern can be automated, and natch it has to be
| one where a function would not do.
+---------------

One that immediately comes to mind for me is the MACRO-10
macros I used in implementing FOCAL-10 [about which I have
written too many times elsewhere], which collected dribbles
of data from the programmer [me] that was distributed
throughout the source code and planted a heavily-munged,
reformatted version of the accumulated data in a single table
at the end... AT COMPILE TIME. This is *very* convenient in
Lisp, whereas user of other languages tend to write various
external pre-processing (think "literate programming") hacks
in Perl or whatever that write out C source files, which then
get compiled/linked with or #include'd in the program.

The advantage of Lisp [or MACRO-10] macros is that the
data being accumulated can *also* be used *while* the
program text is being compiled to further conditionalize
the code being generated, something that's very hard to
do with the external pre-processing approach without having
to "generate" *all* of your program source code from a
meta-source.

[Re-implementing FOCAL in CL -- just to have an illustration
of the above -- is one of those elusive "round tuit" thingies
I haven't gotten yet. (*sigh*)]

Another good example [well, for me it is, though few non-Lispers
might appreciate it] is the definition & use of the VOP, VOP*,
DEFINE-VOP, DEFINE-MOVE-VOP defining macros [and friends] in
the CMUCL compiler. They provide a domain-specific language for
describing virtual instruction sets. From a comment in the source:

;;;; VOP definition structures:
;;;
;;; Define-VOP uses some fairly complex data structures at meta-compile
;;; time, both to hold the results of parsing the elaborate syntax and
;;; to retain the information so that it can be inherited by other VOPs.

;;; The VOP-Parse structure holds everything we need to know about a VOP
;;; at meta-compile time.
;;;

CMUCL thus has its own little [or *not* so little!] compiler-compiler
inside itself.


-Rob

-----
Rob Warnock <rp...@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607

Rob Warnock

unread,
Sep 12, 2007, 10:02:50 PM9/12/07
to
Raymond Wiker <r...@RawMBP.local> wrote:
+---------------

| Emilio Lopes <ec...@gmx.net> writes:
| > Here is my take: a simple macro called `debug', which
| > prints a given *expression* followed by its value.
...
| > Here is the macro definition [in Scheme]:
| > (define-syntax debug
| > (syntax-rules () ...))

| > ((_ expr)
| > (let ((literal (quote expr))
| > (value expr))
| > (display "### ")
| > (display literal)
| > (display ": ")
| > (write value)
| > (newline)
| > value))
| > ((_ expr expr1 ...)
| > (begin
| > (debug expr)
| > (debug expr1 ...)))))
...

| Not a convincing example of the power of (Lisp) macros;
| even C++ can do this: ...

| #define DEBUG(a) (std::cerr << "### " << #a << ": " << (a) << std::endl, a)
+---------------

*BZZZTT!!* Your C++ macro only takes *one* expression;
his takes an *arbitrary* number of expressions in a
single call of the macro!! Let's see you do *that* in C++!!


-Rob

p.s. The one from my personal toolbox, in CL:

> (defmacro dbgv ((&optional (where "Some Unknown Location..."))
&rest forms)
`(progn
(format t "~&DBGV: @~a:~%" ',where)
,@(loop for form in forms
collect `(format t "~s = ~s~%" ',form ,form))))

DBGV
> (dbgv (the-repl) (+ 1 2) (expt 2 100) (strcat "foo" "bar"))

DBGV: @THE-REPL:
(+ 1 2) = 3
(EXPT 2 100) = 1267650600228229401496703205376
(STRCAT "foo" "bar") = "foobar"
NIL
>

I suspose I could make it return the last value like Emilio's does,
but I haven't found the need for that yet.

Brian Adkins

unread,
Sep 12, 2007, 11:37:17 PM9/12/07
to
On Sep 12, 4:25 pm, t...@sevak.isi.edu (Thomas A. Russ) wrote:
> Well, the point here would be: How could you implement that in Ruby IF
> the language didn't already have it in the first place? In other words,
> could you add something that works like that syntactically if the
> compiler writers and language designers HAD NOT ALREADY built it for
> you?
>
> That's the beauty of Lisp macros. You can add them.

What part of "notational convenience", "power", "efficiency", etc. did
you not understand? And stop the shouting already :) You're preaching
to the choir dude. Even as a newbie I can appreciate Lisp's macros -
the programmable programming language, etc.

The op asked for examples of macros to show off to his ignorant
friends/colleagues. Instead of showing off macros that implement
something that *is* already in a particular language, why not show off
a macro that adds something that *isn't* already in the language?
Showing a Ruby programmer a macro that does essentially the same thing
as File.open(filename) {|in| do_something(in) } and then telling him
how lame Ruby is because if the feature wasn't *already in the
language* it would be hard to add, may not be impressive.

Would you agree that languages such as Ruby have traded the
flexibility, generality & uniformity that Lisp provides for notational
convenience, etc.? So why not show a macro example that accentuates
the strength of Lisp and weakness of language X in a particular
context instead of showing one that simply implements a pre-existing
feature?

Ken Tilton

unread,
Sep 13, 2007, 12:28:49 AM9/13/07
to

Barry Margolin wrote:
> In article <zWSFi.1562$Z%7.1...@newsfe12.lga>,
> Ken Tilton <kenny...@optonline.net> wrote:
>
>
>>Christophe wrote:
>>
>>>On 12 sep, 08:59, Eli Bendersky <eli...@gmail.com> wrote:
>>>
>>>
>>>>Hello all,
>>>>
>>>>In short: I'm looking for a "killer" macro - a macro (or a couple of
>>>>related ones) that show what Lisp can do and other languages (those
>>>>without uniform syntax) can not.
>>
>>....
>>
>>
>>>No needs to search a long time : loop, iterate or defclass are smart
>>>examples.
>>
>>You are absolutely right, but others will say, "Hunh? Those are part of
>>the language. You think implementing classes in C++ is easy?"
>
>
> LOOP was not originally part of the language, it was a user-written
> macro in MacLISP. And even though it's now part of the standard, it's
> still implemented pretty much the same way (I'll bet that at least 75%
> of the code in most LOOP implementations is from the original MIT LOOP).
>
> And ITERATE is not a part of the language.
>

Deftly missing my entire point, thus nicely reinforcing my point that
even lucid points have little chance against minds that already know
what they are reading before they read it.

corey....@gmail.com

unread,
Sep 13, 2007, 2:05:42 AM9/13/07
to
On Sep 12, 2:59 am, Eli Bendersky <eli...@gmail.com> wrote:
> Hello all,
>
> In short: I'm looking for a "killer" macro - a macro (or a couple of
> related ones) that show what Lisp can do and other languages (those
> without uniform syntax) can not.
>
> A longer version: Many contemporary languages boast their sharing most
> of the functional features of Lisp. Perl, Ruby, Javascript - all have
> convenient lists (or arrays), functions as first class objects,
> garbage collection, dynamic typing, lexical closures. However, one
> thing they lack is uniform syntax, and hence the service of a powerful
> built-in macro system such as Common Lisp's "defmacro".
> When confronted by fellow programmers with the question "so why is
> Lisp so special", I'm looking for that short - not too hard to

> understand - snippet of code that will show them *why*. I'm convinced
> that such a snippet must involve macros. But all the examples I ran
> into so far have been either too simple - and could be easily
> implemented another way (for example with Ruby's blocks), or too
> complicated.
>
> Any suggestions for such an example ?
>
> Eli

The "choices sample Logo -> Lisp" discussion might make a nice
example.

The thread left off at:

On Sep 5, 2:00 pm, t...@sevak.isi.edu (Thomas A. Russ) wrote:
> Brian Adkins <lojicdot...@gmail.com> writes:
> > I also discovered &optional, so the final version is:
>
> > (defun choices (menu &optional (sofar '()))
> > (if menu
> > (dolist (i (car menu))
> > (choices (cdr menu) (append sofar (list i))))
> > (format t "~{~a~^ ~}~%" sofar)))
>
> And if you want to improve the efficiency for longer lists, then using
> the idiom (do they call them design patterns now?) of consing onto the
> front of the list and reversing instead of appending, which I recall
> someone else used.
>
> (defun choices (menu &optional (sofar '()))
> ;; This builds up the list in reverse order and then reverses the
> ;; result just before printing. Can't use NREVERSE because SOFAR is
> ;; shared among many branches.
> (if menu
> (dolist (i (car menu))
> (choices (cdr menu) (cons i sofar)))
> (format t "~{~a~^ ~}~%" (reverse sofar))))


>
> --
> Thomas A. Russ, USC/Information Sciences Institute

Macros make it pretty easy to extend that solution to make it cover
anything else you might want to do while iterating "choices":

(defmacro dochoices ((choice menu) &body body)
(let ((choices (gensym))
(sofar (gensym))
(inner-menu (gensym))
(i (gensym)))
`(labels ((,choices (,inner-menu &optional (,sofar '()))
(if ,inner-menu
(dolist (,i (car ,inner-menu))
(,choices (cdr ,inner-menu) (cons ,i ,sofar)))
(let ((,choice (reverse ,sofar)))
,@body))))
(,choices ,menu))))

(defun choices (menu)
(dochoices (choice menu)
(format t "~{~a~^ ~}~%" choice)))

Corey

Madhu

unread,
Sep 13, 2007, 2:13:05 AM9/13/07
to
Helu

* "Dimiter <mal...@gmail.com> <5kr6aeF...@mid.individual.net> :

| For a code that was doing LZO decompression, I've added the following
| macros, that were "simulating" how for example "*p++ = value", or
| "*p++ = *x++", or memcpy(). Granted not the best macros, but minimized
| my code.
|

<SNIP>


| Here is an example of how to use them
|
| The following would copy 15 bytes from the source-array starting from
| the source-index, in the dest-array, starting from the dest-index. It
| would then increase dest-index and source-index by 15.
|
| (acpy++ dest-array dest-index source-array source-index 15)

Just FYI that should equivalent to

(replace dest-array source-array :start1 dest-index :start2
:source-index :end2 (+ source-index 15))

or using a macro:
(setf (subseq dest-array dest-index)
(subseq source-array source-index (+ source-index 15)))

[untested]

| Also note that this would check whether the number of indexes is
| constant, and in the case above it is (15), so it would actually
| unroll that, instead of loop it.

--
Madhu

pineapp...@yahoo.com

unread,
Sep 13, 2007, 3:04:31 AM9/13/07
to
I was asked a while back to code up a poker simulation for a project.
I did this in Smalltalk, which was quite a nice language for a
programmer to work in, especially for this type of problem, and I was
able to do the work much easier, quicker, and with much less headache
than in a "conventional" language - say Java (barf).

However, upon reevaluating the solution and reflecting on it, I saw
where another language could have done "the core" of the program (hand
evaluation) much better, and in much less length of code, with far
less possibility (none) of bugs. This language was Prolog. I re-
implemented "the core" in Prolog, and got at least a 20-1 reduction in
the size of the code there. In fact, all the code for "the core"
could fit on one page of Prolog. Better still, when I ran the new
code against the old code for the same inputs (given 7 cards,
evaluate, and return the best 5 card hand), it "disagreed" with the
old code on about 5 hands out of several million randomly generated
hands, and spit out different solutions for those hands. When I
analyzed the differences, I found that the Prolog solution was correct
in those cases. It was due to a few hard-to-find bugs in the old
solution, which was to be expected simply because that code was much
longer and more complex. So the new Prolog solution, because of its
simplicity, was also more correct, and uncovered bugs in the old
solution.

To illustrate the conciseness of the Prolog solution, here is the code
to recognize a "4 of a kind":

quad(A,B,C,D,E,F,G) :- permutation([A,B,C,D,E,F,G],[X,X,X,X,_,_,_]).

That's it - one line of code, and the input doesn't even need to be
sorted beforehand, as the old solution's input did, so I get to remove
an unnecessary sort.

At any rate, if I didn't know Prolog, and if someone showed me this
solution as compared with my old solution, I'd be quite impressed -
impressed enough to learn Prolog right away, or at least look at it.
The point being, I am impressed with a language if it provides for
solving problems in as short a length of code as possible, with the
least amount of complexity as possible, while still being completely
readable, intuitive, understandable, bug-free and correct. 1 line of
code beats a page or several pages of code any day.

APL (and its successor - J) has a reputation for being able to produce
amazingly short solutions for some problems. I saw a one line APL
program for Conway's Game of Life. The problem I have with APL (and
J) is the "while still being completely readable" element I mentioned
above.

Anyway, the point: Can you find an example of being able to do
something in Lisp in 1) much shorter length of code, AND/OR 2) much
less complexity, than many "conventional" languages could do it?
Perhaps the best way to prune the list of possible things to examine
is to first think about those couple of things about Lisp that are
particularly strong, or make it stand out, or make it different than
other languages, and then look for classes of problems which those
strengths/differences can be applied. For instance, with Prolog, its
strengths and differences are pattern-matching, unification, and
backtracking. With the above solution, it doesn't even use its stand-
out feature of backtracking - it's simply all pattern-matching/
unification. For APL/J, it excels at doing array/matrix operations in
parallel, with no need for explicit looping constructs.

Lisp excels at _____ (fill in the blank). Now you've at least
eliminated some possibilities in your search.

(Maybe you already filled-in the blank with this macro capability you
spoke of. However, if it is difficult to find something here dramatic
enough to impress your friends, perhaps you can list other Lisp
"standout" features? Or perhaps Lisp doesn't have one thing ultra-
dramatic in one particular category, but a lot of less dramatic things
in many categories, and these things all add up in the end? If
nothing else, at least Lisp's syntax isn't nearly as grotesque and
burdensome as something like Java's. Come on - look at a Java "Hello
World" program, with the class declaration, the "public static void
main," the "System.blah.whatever.printblah" hideousness, etc. - but
then again if it hasn't already occurred to a Java programmer to hate
all of that stuff, you are probably barking up the wrong tree with
trying to convert that particular person.)

Regards.

Eli Bendersky

unread,
Sep 13, 2007, 4:45:29 AM9/13/07
to
> Alexanderscu's "Modern C++ Design" is basically exactly about this:
> implementing "patterns" (in the Gang of Four sense) in code. Except he
> uses a weak "macro" language to express these solutions. You could go
> through the book and pick out few and show how they are much easier to
> express with Lisp macros.

"Modern C++ Design" is a piece of monstrosity. It's what turned me
away from C++, actually. I just kept reading it and thinking - "my
god, should this really be so hard ? should I really use all these
ugly tricks to fool the compiler in order to write normal code ?"

That said, maybe reimplementing the horrors of Modern C++ Design in
CL is a good idea.

Eli Bendersky

unread,
Sep 13, 2007, 4:54:22 AM9/13/07
to

Thanks for this, it is exactly what I intended to write. These
examples just show something that's slightly more elegant in Lisp than
in Ruby. Hardly a reason for all Lispers to brag how superior their
language is.

Unfortunately, 40+ posts into this thread, there still is no real
answer.

Eli

Rainer Joswig

unread,
Sep 13, 2007, 5:05:54 AM9/13/07
to
In article <ufy1jc...@nhplace.com>,

I agree, that's useful.

>
> > One I also want to see on the stack. Directly. With
> > useful parameters. I could for example extract the DO-SOMETHING
> > function in a debugger and apply it to something. WITH-OPEN-FILE
> > is invisible on the stack.
>
> Well, not the way I generally do it, so I mostly don't see this deficiency.
> (Note: I didn't say "I don't see this AS a deficiency", I mean it really
> isn't one because we (you and I) both see the same thing on the stack,
> since my macro call often expands into the function call that you wrote
> explicitly, and so at the time of looking on the stack, the textual pedigree
> of the call is long-since erased and the two techniques are blurred by
> a common functional implementation.)
>
> Put another way, I often start with the implementation of the function you like
> and THEN write the macro because I hate its syntax. You seem to be assuming
> I'm starting with a blank sheet and first writing a macro. But in my case,
> at least, that's often not true. It probably happens sometimes, but more
> and more, it's rare, and I have good [private] macrology for managing this.

I usually develop both together, experimenting a bit with the approaches
and try to find something that's a compromise of being elegant, minimal,
good to read and easy to debug. ;-) Sometimes I even might achieve that
goal. Macros as 'minimal art'.

> > It creates some weird code that
> > has no simple representation. Worse. it might be completely replaced
> > with a bunch of new things I would never associate with WITH-OPEN-FILE.
> > When I want to edit source for the stack frame, it somehow has to figure
> > out that there was some macro call and find it.
>
> I just edit the thing on the stack frame and it takes me to the function that
> is textually adjacent to the macro.

I wish everybody would organize the code like you do. ;-)
Others please take that as good advice! ;-)

...

> > I'm willing to
> > give that up for IF, WHILE, LOOP and others - but somehow not
> > for CALL-WITH-OPEN-FILE.
>
> Heh. Isn't the brain a marvelous thing? :) It just goes to underscore
> my argument that the right simplicity metric for a language is not
> "textual minimality" but rather "structural isomorphism to how people
> think" (even knowing that this varies person to person, and that it's
> hard to figure out precisely ... but it helps explain why a Lisp2 is a
> rational concept). I don't doubt you have just such an oddly placed
> line in your head--I'm sure I have such a thing in a different place.
> It's natural and normal.

Common Lisp is certainly a language that my thinking is comfortable with.
But it is more than the language. It is also the way you work
with the symbols of the program (in a general sense). For me
the environment is best when I have everything directly accessible,
no unnecessary hiding, things are descriptive and 'first class'.
C (and others) puts a wall between me and the data/program.
Common Lisp enables environments that let me work interactively.
I always feel pain when M-. is not working for some code or when things
look cryptic at runtime. The more I'm thinking about it in the last years,
the more I like 'symbolic programming'. Also 'Symbolics programming'.
;-)

> > > I am NOT trying to say that you should believe these arguments, but can you
> > > see how some of these things would matter to others?
> >
> > Sure.
>
> Phew. :)

Relax!

--
http://lispm.dyndns.org

Eli Bendersky

unread,
Sep 13, 2007, 5:11:41 AM9/13/07
to

Alan,

This is an interesting example, but except for the minor nicety of
opcode numbers growing automatically, it looks just like a
sophisticated way to work around the limitations of the "case" macro.
Why not just write an "evaluating case" macro and get done with it ?
Writing macros to overcome problems in other macros isn't a good
example of a language's power. Besides, someone can correctly note he
just does this easily in C:

#define LOAD 0
#define STORE 3

#define ADD STORE+1
#define SUBTRACT STORE+2


Eli Bendersky

unread,
Sep 13, 2007, 5:24:31 AM9/13/07
to

Emilio's example is indeed a good one, and thanks for showing its CL
translation. However, I think that in most cases the single expression
debug is sufficient, and this is indeed doable in C++.

Eli

Eli Bendersky

unread,
Sep 13, 2007, 5:25:41 AM9/13/07
to

While these are nice macros, they're just a way to hide the lambdas,
and Ruby has a built in syntax for such things, so it's not too
convincing.

Eli


Eli Bendersky

unread,
Sep 13, 2007, 5:30:49 AM9/13/07
to
> Lisp excels at _____ (fill in the blank). Now you've at least
> eliminated some possibilities in your search.
>
> (Maybe you already filled-in the blank with this macro capability you
> spoke of. However, if it is difficult to find something here dramatic
> enough to impress your friends, perhaps you can list other Lisp
> "standout" features? Or perhaps Lisp doesn't have one thing ultra-
> dramatic in one particular category, but a lot of less dramatic things
> in many categories, and these things all add up in the end? If
> nothing else, at least Lisp's syntax isn't nearly as grotesque and
> burdensome as something like Java's. Come on - look at a Java "Hello
> World" program, with the class declaration, the "public static void
> main," the "System.blah.whatever.printblah" hideousness, etc. - but
> then again if it hasn't already occurred to a Java programmer to hate
> all of that stuff, you are probably barking up the wrong tree with
> trying to convert that particular person.)
>
> Regards.

But you see, as I mentioned in my original post, Lisp really stands
out only in its macros, because all the rest was already implemented
in other languages. Yes, 20 years ago Lisp was amazing - compared with
C, Fortran and Pascal, but these days with Perl, Ruby and even C# 3.0
around, the only thing that *really* sets Lisp apart is macros.

Eli

Rainer Joswig

unread,
Sep 13, 2007, 5:35:04 AM9/13/07
to
In article <1189673662....@y42g2000hsy.googlegroups.com>,
Eli Bendersky <eli...@gmail.com> wrote:


"Sie sehn den Wald vor lauter Baumen nicht."

--
http://lispm.dyndns.org

Rainer Joswig

unread,
Sep 13, 2007, 6:34:21 AM9/13/07
to
In article <1189675849.0...@19g2000hsx.googlegroups.com>,
Eli Bendersky <eli...@gmail.com> wrote:

> > Lisp excels at _____ (fill in the blank). Now you've at least
> > eliminated some possibilities in your search.
> >
> > (Maybe you already filled-in the blank with this macro capability you
> > spoke of. However, if it is difficult to find something here dramatic
> > enough to impress your friends, perhaps you can list other Lisp
> > "standout" features? Or perhaps Lisp doesn't have one thing ultra-
> > dramatic in one particular category, but a lot of less dramatic things
> > in many categories, and these things all add up in the end? If
> > nothing else, at least Lisp's syntax isn't nearly as grotesque and
> > burdensome as something like Java's. Come on - look at a Java "Hello
> > World" program, with the class declaration, the "public static void
> > main," the "System.blah.whatever.printblah" hideousness, etc. - but
> > then again if it hasn't already occurred to a Java programmer to hate
> > all of that stuff, you are probably barking up the wrong tree with
> > trying to convert that particular person.)
> >
> > Regards.
>
> But you see, as I mentioned in my original post, Lisp really stands
> out only in its macros, because all the rest was already implemented
> in other languages.

Unfortunately in hundreds of languages. In Lisp you can have
it in one language.

Sure with XML now everybody can describe data (and even procedures)
with a prefix language. Unfortunately the mechanisms are
ugly, monstrous and much less integrated.

In Lisp I write

(service-call "SVCL"
(04 18 customer-name)
(19 23 customer-id)
(24 27 call-type-code)
(28 35 date-of-call-string))

and READ reads it. Plain and simple. No monstrous XML
parser. If I want to do something with it I apply
a function. If I want to compile it with the compiler
to some efficient procedure, I write a macro and
add it to the expression:

(DEFMAPPING service-call "SVCL"
(04 18 customer-name)
(19 23 customer-id)
(24 27 call-type-code)
(28 35 date-of-call-string))

Still I'm able to READ it, pretty print it, transform it.
All with mildly simple mechanisms.

> Yes, 20 years ago Lisp was amazing - compared with
> C, Fortran and Pascal, but these days with Perl, Ruby and even C# 3.0
> around, the only thing that *really* sets Lisp apart is macros.
>
> Eli

That's plain wrong.

Macros are available in other programming languages, too.

Common Lisp is a multi-paradigm language that enables
interactive software development of small to very
large programs with somehow useful efficiency.
At its core it is very extensible to be able to
add very different programming paradigms.

PERL, Ruby and C# are very different. In Common Lisp
you can do most that those can in one package.
Ruby is totally inefficient (besides what is implemented
in C in its core) and there is little hope that it
can be better. You can write most Ruby stuff relatively
easy in Lisp, but if you try to use Ruby for Lisp
software be prepared for a hard time.
C# is a static language in the
tradition of C, C++ and others with more complication
bolted on the totally wrong base language. Sure
you can have closures in C#, but the result is ugly.
I you bolt wings on Frankenstein's monster, do you
think it will be a beautiful bird?

I try to minimize my writing of macros, that's not the single
important feature for me. I have not found
any environment for Ruby, Perl or C# that works
like Allegro CL, MCL, LispWorks or even Symbolics Genera.
The only ones that are close are Smalltalk environments
and some less known others.

--
http://lispm.dyndns.org

Aleksander Nabagło

unread,
Sep 13, 2007, 7:10:22 AM9/13/07
to
Pascal Costanza wrote:
> Thomas F. Burdick wrote:
>> On Sep 12, 10:28 am, Pascal Costanza <p...@p-cos.net> wrote:
>>> Eli Bendersky wrote:
>>>
....

>> withOutputStream abstracting away the need for ensure: (ie, unwind-
>> protect), and doInputLines: abstracting away that plus the loop of
>> reading line-by-line while input is available. While abstracting away
>> the lambdas is a win for Lisp, that's not universal, so at least a
>> doubtful ST'er won't see anything there.
>
> That's why I said "my best attempt so far", and not "a good attempt." ;)
>
> The two examples in that blog entry crystallize what I personally think
> is the essence of macros. I also noticed that many people don't get the
> second example though. It'd be nice to see another example that's
> equally compact and conveys the same idea.

;;; horner-ex.lisp
(defun horner-ex (x aa)
(flet ((hrn (a b) `(+ ,a (* ,x ,b))))
(labels ((hrn-re (aa)
(if (cdr aa)
(hrn (car aa) (hrn-re (cdr aa)))
(car aa))))
(hrn-re aa))))

(defmacro horner-def (h x &rest r)
`(defun ,h (,x) ,(horner-ex x r)))

;; Dribble of #<IO TERMINAL-STREAM> started 2007-09-13 13:01:49
#<OUTPUT BUFFERED FILE-STREAM CHARACTER #P"horner.txt">
[2]> (load "horner-ex.lisp")
;; Loading file horner-ex.lisp ...
;; Loaded file horner-ex.lisp
T
[3]> (horner-def alfa x 1 2 3)
ALFA
[4]> (alfa 2)
17
[5]> (alfa 3)
34
[6]> (symbol-function 'alfa)
#<FUNCTION ALFA (X) (DECLARE (SYSTEM::IN-DEFUN ALFA)) (BLOCK ALFA (+ 1
(* X (+ 2 (* X 3)))))>
[7]> (horner-def alfa y 2D0 (/ 7D0) 4D0 5D0)
ALFA
[8]> (alfa 2D0)
58.285714285714285d0
[9]> (alfa 3D0)
173.42857142857144d0
[10]> (symbol-function 'alfa)
#<FUNCTION ALFA (Y) (DECLARE (SYSTEM::IN-DEFUN ALFA))
(BLOCK ALFA (+ 2.0d0 (* Y (+ (/ 7.0d0) (* Y (+ 4.0d0 (* Y 5.0d0)))))))>
[11]> (exit)

--
A
.

ar...@tidexsystems.com

unread,
Sep 13, 2007, 8:21:00 AM9/13/07
to
Eli Bendersky <eli...@gmail.com> writes:

Speaking of Alexandrescu, it's worth mentioning that he is also the
author of ScopeGuard, a well-known C++ library that makes writing
exception-safe code easier.

Some time ago, ScopeGuard was discussed on c.l.c++.m. Alexandrescu
says about ScopeGuard (Message-ID <IrEDKt.
5...@beaver.cs.washington.edu>):

> ScopeGuard is a kludge invented in absence of on_scope_xxx. We've even
> written a macro, ON_BLOCK_EXIT, that awkwardly emulates on_scope_exit.
> ScopeGuard is limited and awkward because it lacks access to the current
> scope; often you need to stop doing what you're doing just to write some
> little function that doesn't help anywhere else.

In another post, he says (Message-ID
<IrG7B...@beaver.cs.washington.edu>):

> Again, ScopeGuard is a lame paliative for the highly desirable
> on_scope_xxx feature. Its usefulness comes from us needing and lacking
> the true on_scope_xxx feature.

A few posts later, Common Lisp's unwind-protect operator is mentioned.
According to Alexandrescu, the problem with unwind-protect, and with
any nested structure for an on_scope_xxx construct that includes
normal code within it, is that it doesn't scale. (Message-ID
<IrEMJ...@beaver.cs.washington.edu>)

Alexandrescu would like something like the following to work:

(defun hello-world ()
"Hello, world."
(on-exit (format t "exiting...~%"))
(guard foo (format t "guard...~%"))
(unless (y-or-n-p "run guard")
(dismiss foo))
(format t "hello, world~%"))

On-exit is supposed to queue the form(s) to run on scope exit; guard
is supposed to do the same, but also give the form(s) a name so that
they can be selectively dismissed. In-scope (use not shown here)
establishes a scope, and defun should implicitly establish such a
scope, too.

This feature is easily implemented with macros (note that defun is
shadowed):

(defmacro in-scope (&body body)
(let ((guards (gensym "GUARDS-")))
`(let ((,guards '()))
(unwind-protect
(macrolet ((guard (name &body body)
`(push (cons ',name (lambda
() ,@body)) ,',guards))
(dismiss (name)
`(setf ,',guards
(delete ',name ,',guards :key #'car)))
(on-exit (&body body)
`(push (cons '#:anon (lambda
() ,@body)) ,',guards)))
,@body)
(dolist (guard ,guards)
(funcall (cdr guard)))))))

(defmacro defun (name lambda-list &body body)
`(cl:defun ,name ,lambda-list
,@(if (stringp (car body)) (list (car body)) '())
(in-scope ,@(if (stringp (car body)) (cdr body) body))))

How would non-Lispers handle this?

Ariel

pineapp...@yahoo.com

unread,
Sep 13, 2007, 9:32:52 AM9/13/07
to
> "Modern C++ Design" is a piece of monstrosity. It's what turned me
> away from C++, actually.

What turned me away was sitting with a Stroustroup C++ book in hand,
staring at the STL examples for hours, trying to get a simple iterator
to iterate over a damn container class. A day later, when I finally
had the syntax correct, the iterator scrolled off the screen
(something like <std::blah::blah>>blah.blah.blah...), and worse I had
to cut and paste the damn thing every time I wanted to use it. Then I
glanced over at Smalltalk where I saw a simple "x do:[whatever]" to
iterate over a collection, said "screw this" and threw the C++ book
across the room, and never looked back. This, by the way, is coming
from someone who once wrote a C compiler for a project in college.

<<That said, maybe reimplementing the horrors of Modern C++ Design in
CL is a good idea. >>

Please... no... I beg you....

pineapp...@yahoo.com

unread,
Sep 13, 2007, 9:35:30 AM9/13/07
to
> Hardly a reason for all Lispers to brag how superior their
> language is.
>
> Unfortunately, 40+ posts into this thread, there still is no real
> answer.

Perhaps there really is no "answer" to speak of. Perhaps the answer
is simply "you like lisp, so program in lisp, and leave others to
their own devices."

Jeronimo Pellegrini

unread,
Sep 13, 2007, 10:18:49 AM9/13/07
to
On 2007-09-13, pineapp...@yahoo.com <pineapp...@yahoo.com> wrote:
>> "Modern C++ Design" is a piece of monstrosity. It's what turned me
>> away from C++, actually.
>
> What turned me away was sitting with a Stroustroup C++ book in hand,
> staring at the STL examples for hours, trying to get a simple iterator
> to iterate over a damn container class. A day later, when I finally
> had the syntax correct, the iterator scrolled off the screen
> (something like <std::blah::blah>>blah.blah.blah...), and worse I had
> to cut and paste the damn thing every time I wanted to use it. Then I
> glanced over at Smalltalk where I saw a simple "x do:[whatever]" to
> iterate over a collection, said "screw this" and threw the C++ book
> across the room, and never looked back. This, by the way, is coming
> from someone who once wrote a C compiler for a project in college.

I have been trying to move away from C++ for other reasons, and I don't
like C++, but iterating is not supposed to be hard:

#include <boost/foreach.hpp>
#define foreach BOOST_FOREACH
...
std::vector<some-type> v;
...
foreach(some-type element, v) {
...
}
some-type el;
foreach(el, v) {
...
}

Works for anything that supports iterators.

My problem with C++ is on other areas...
It's hard to debug, build systems are a pain (it's not interactive), the
exception mechanism is not good, metaprogramming is insane (you can use
macros like boost's foreach easily, but trying to create your own macros
as you do in Lisp will drive you nuts), and other languages seem to allow
for cleaner code.

J.

Eli Bendersky

unread,
Sep 13, 2007, 10:30:22 AM9/13/07
to
On Sep 13, 12:34 pm, Rainer Joswig <jos...@lisp.de> wrote:
> In article <1189675849.006869.263...@19g2000hsx.googlegroups.com>,

> Eli Bendersky <eli...@gmail.com> wrote:
>
>
>
> > > Lisp excels at _____ (fill in the blank). Now you've at least
> > > eliminated some possibilities in your search.
>
> > > (Maybe you already filled-in the blank with this macro capability you
> > > spoke of. However, if it is difficult to find something here dramatic
> > > enough to impress your friends, perhaps you can list other Lisp
> > > "standout" features? Or perhaps Lisp doesn't have one thing ultra-
> > > dramatic in one particular category, but a lot of less dramatic things
> > > in many categories, and these things all add up in the end? If
> > > nothing else, at least Lisp's syntax isn't nearly as grotesque and
> > > burdensome as something like Java's. Come on - look at a Java "Hello
> > > World" program, with the class declaration, the "public static void
> > > main," the "System.blah.whatever.printblah" hideousness, etc. - but
> > > then again if it hasn't already occurred to a Java programmer to hate
> > > all of that stuff, you are probably barking up the wrong tree with
> > > trying to convert that particular person.)
>
> > > Regards.
>
> > But you see, as I mentioned in my original post, Lisp really stands
> > out only in its macros, because all the rest was already implemented
> > in other languages.
>
> Unfortunately in hundreds of languages. In Lisp you can have
> it in one language.
>

This is a good point. But it's true because of macros. With macros and
Lisp's uniform syntax, I can not only write a library that implements
anything I might deem important, but also make it look
indistinguishable from native Lisp - extend the language itself.

> Sure with XML now everybody can describe data (and even procedures)
> with a prefix language. Unfortunately the mechanisms are
> ugly, monstrous and much less integrated.
>
> In Lisp I write
>
> (service-call "SVCL"
> (04 18 customer-name)
> (19 23 customer-id)
> (24 27 call-type-code)
> (28 35 date-of-call-string))
>
> and READ reads it. Plain and simple. No monstrous XML
> parser. If I want to do something with it I apply
> a function. If I want to compile it with the compiler
> to some efficient procedure, I write a macro and
> add it to the expression:
>
> (DEFMAPPING service-call "SVCL"
> (04 18 customer-name)
> (19 23 customer-id)
> (24 27 call-type-code)
> (28 35 date-of-call-string))
>
> Still I'm able to READ it, pretty print it, transform it.
> All with mildly simple mechanisms.

Yes, but this already exists in other languages. Javascript has JSON
which is almost like the Lisp way of storing data in s-expressions.
YAML is also popular (and 99% equivalent to JSON).

>
> > Yes, 20 years ago Lisp was amazing - compared with
> > C, Fortran and Pascal, but these days with Perl, Ruby and even C# 3.0
> > around, the only thing that *really* sets Lisp apart is macros.
>
> > Eli
>
> That's plain wrong.
>
> Macros are available in other programming languages, too.
>
> Common Lisp is a multi-paradigm language that enables
> interactive software development of small to very
> large programs with somehow useful efficiency.
> At its core it is very extensible to be able to
> add very different programming paradigms.
>

Yes, true - "at its core it is very extensible" *because* of macros.

> PERL, Ruby and C# are very different. In Common Lisp
> you can do most that those can in one package.
> Ruby is totally inefficient (besides what is implemented
> in C in its core) and there is little hope that it
> can be better. You can write most Ruby stuff relatively
> easy in Lisp, but if you try to use Ruby for Lisp
> software be prepared for a hard time.

Leaving inefficiency apart for the moment[1], Ruby has anything Lisp
has except macros. Ruby is the latest and greatest in the evolution
path of modern programming languages and their convergence path with
Lisp. Again, I'll use Norvig's "seven points that make Lisp
different".

Ruby:

* It has a convenient built-in syntax for arrays and hash tables
* Garbage collection
* Dynamic typing - in fact the closest to Lisp yet (except maybe
Smalltalk, with which I'm not familiar) with its duck typing
philosophy
* First class functions, with the syntactic sugar called "blocks"
which makes a lot of higher-order functional paradigms very convenient
* Interactive Environment - Ruby's "irb" is in many ways equivalent to
the REPL. And Ruby slowly but surely gets the support of good IDEs -
Emacs, Eclipse, Visual Studio, NetBeans and so on.
* Extensibility - everything is modifiable, full reflection
capabilities - you can even change built-in classes on the fly.
module_eval and class_eval allow for much safer code generation than
the string eval of Perl. Ruby is open enough to be the most convenient
language, after Lisp of course, for writing DSLs.

* Uniform syntax: Bzzzzt! Nope. None, and so no *real* macros either.
Ruby loses to Lisp on this point, and this is a big point,
admittedly.

But when you tell people about this last point, they say - fine, but
what's so great about these macros.
And so we come back to the original reason for my starting this
thread!

Eli


[1] I see no reason why Ruby should be inherently less efficient than
any other interpreted language. With its new VM in development, it has
hope yet. And anyway, performance alone can't play that big a role.
Ruby's interoperability with C is much simpler to work out than CL's
CFFI, and writing critical code in C is very possible.

philip....@gmail.com

unread,
Sep 13, 2007, 11:11:19 AM9/13/07
to

[Rainer has already explained this very eloquently but I'll have
another go...]

I use macros fairly infrequently but when I do use them, I feel
they're the best solution to the problem I'm trying to tackle. But
Common Lisp is more than just macros just as Ruby is more than just
Rails and Python is more than just funny indentation rules. After
working in many different languages over the years, Lisp offers me the
most choices all contained in a pretty consistent whole. This is the
beauty of the language. Macros are but one part of that.

--
Phil
http://phil.nullable.eu/

Rainer Joswig

unread,
Sep 13, 2007, 11:45:53 AM9/13/07
to
In article <1189693822....@g4g2000hsf.googlegroups.com>,
Eli Bendersky <eli...@gmail.com> wrote:

Macros is only the packaging. It makes the tools more
programmer-friendly.

No, it is because Common Lisp has extensibility at many points built in.
But it also tries to find the balance between extensibility and
efficiency. The idea that code can be compiled is built into the
language. If you read the language spec, you will see this in many
places. Common Lisp is also a 'language'. Where Ruby and all the others
mix language definition and implementation reference. What ever
has been implemented in the lead implementation is suddenly
part of the language. Not so with Common Lisp. The language
is defined with the freedom for implementors to try widely
different implementation strategies. For example Common Lisp
as a language says nothing about sutomatic memory management. Still
most implementations will provide one.

Where can you extend / modify Common Lisp?

* you can create your own dialects in packages, reusing from other
packages what is useful.

* you can change the interning of data via READ MACROS.
This allows you also to change/extend the surface syntax.

* you can change the handling of Errors via the Condition System

* you can change the implementation of classes via meta classes

* you can change the printed representation via writing methods
for print objects

* you can change the interpretation of expressions via macros

* you can write your own optimizers via compiler-macros

* you can compile any code at runtime

* you can interpret any code at runtime

* you can implement your own debugging tools via hooks

* you can change the way inheritance works

* you can change the way methods are combined

* you can shadow values via dynamic binding

* you can express hints to the compiler, so that
the same code might run with totally different settings.

* you can execute arbitrary code at read time (if you allow that),
compile time and load time. That has vast consequences. Instead of
developing some template language, you can directly execute
your Lisp code in the compile time environment.

Remember, this just the language definition. Above
will be portable over implementations.

If you look at individual implementations, you will see
even more possibilities. Even more for those who are
completely written in Lisp (including the compiler).

> > Sure with XML now everybody can describe data (and even procedures)
> > with a prefix language. Unfortunately the mechanisms are
> > ugly, monstrous and much less integrated.
> >
> > In Lisp I write
> >
> > (service-call "SVCL"
> > (04 18 customer-name)
> > (19 23 customer-id)
> > (24 27 call-type-code)
> > (28 35 date-of-call-string))
> >
> > and READ reads it. Plain and simple. No monstrous XML
> > parser. If I want to do something with it I apply
> > a function. If I want to compile it with the compiler
> > to some efficient procedure, I write a macro and
> > add it to the expression:
> >
> > (DEFMAPPING service-call "SVCL"
> > (04 18 customer-name)
> > (19 23 customer-id)
> > (24 27 call-type-code)
> > (28 35 date-of-call-string))
> >
> > Still I'm able to READ it, pretty print it, transform it.
> > All with mildly simple mechanisms.
>
> Yes, but this already exists in other languages. Javascript has JSON
> which is almost like the Lisp way of storing data in s-expressions.

Javascript is in many ways like Lisp. It even has EVAL.

> YAML is also popular (and 99% equivalent to JSON).

Another language. Another tool.

We have JavaScript, Ruby, Rebol, Perl, Python, ... you name it.
All somewhat similar. All somewhat different. Each has some
special feature. Each lacks one. All are inefficient for
some types of application development. Common Lisp is
also inefficient for some types of application development,
but the range of possibilities is much larger.

> Yes, true - "at its core it is very extensible" *because* of macros.

No, macros are only the packaging. You might want to check
out 'The Art of the Metaobject-Protocol. It describes
CLOS and the Meta-object Protocol and even gives a simple
implementation. CLOS has a layered architecture. At the
core is CLOS itself (CLOS is written in CLOS), on top
of that is a functional layer and the Top-Level
macro layer makes it more convenient to use. But the macros
are not the implementation. They are the 'sugar'.

Sure, but most Lisp people I know prefer to write Lisp and not C.

There is no such thing like an 'interpreted language' Every
language can be interpreted. There are interpreters for C
and similar languages.

Implementations can be based on an Interpreter. When people say Ruby,
say mean mostly the lead implementation and the language it implements.
With Common Lisp the Spec is the lead and the implementations
try to compete to implement it 'best' and give the users useful
extensions. Common Lisp implementations have all kinds of
implementation strategies. Some will provide you with special
batch compilers which will use some of the assumptions they
are allowed to make.

With Ruby, since it is implemented in C, interoperability with C is
good (so is it with GCL, CLISP, ECLS and other Common Lisp which
are either implemented in C or compile to C). But Common Lisp
does not say to you if you want to write something somehow
efficient you need to use C. I'd say Common Lisp is much more
self-respecting as Ruby - the consequence is that many
Common Lisp implementations are written in Common Lisp. And not in C.

--
http://lispm.dyndns.org

fortunatus

unread,
Sep 13, 2007, 1:35:27 PM9/13/07
to
I think we should explore how the same things can be done in other
languages to highlight how it is nicer in Lisp.

A Lisp macro lets one
a)write a function to write Lisp code at read-time,
b)allow arguments to the function that include executable code,
b)in such a way that the macro call looks like normal code.

In other languages, C for instance, this kind of functionality is
done with "pre-processors". The standard preprocessor fits in a
macro call that looks like a C function call. The Objective C
preprocessor
writes all kinds of C code and the "macro calls" look like additional
syntax rather than regular C code.

The closest we could come to having a C macro system that is as
nice as the Lisp macro system is to provide a framework for writing
preprocessors. Such a framework would include a C parser, and a way
to fit the macro call syntax into the parser as either keywords, and/
or
function names, and/or operator syntax, et cetera.

Thinking along these lines, it seems to me that what is nice about
Lisp is this is all pulled together already in the basic language.
The elements include:
1) the regularized syntax
2) the reader
3) defmacro

Dimiter "malkia" Stanev

unread,
Sep 13, 2007, 1:50:08 PM9/13/07
to
Yup! I've actually tried them, but for this particular case, I needed
performance as much as possible, and unfortunately I was unable to get
replace or subseq working fast enough for me. Actually I don't think
I've tried subseq well enough, but replace on most LISP compilers was a
function call, not really optimal to call it when comes to couple of
average transfer.

I would definitely check subseq again :)

Thomas A. Russ

unread,
Sep 13, 2007, 12:24:35 PM9/13/07
to
Jeronimo Pellegrini <j...@aleph0.info> writes:

> On 2007-09-13, pineapp...@yahoo.com <pineapp...@yahoo.com> wrote:
>
> I have been trying to move away from C++ for other reasons, and I don't
> like C++, but iterating is not supposed to be hard:
>
> #include <boost/foreach.hpp>
> #define foreach BOOST_FOREACH
> ...
> std::vector<some-type> v;
> ...
> foreach(some-type element, v) {
> ...
> }
> some-type el;
> foreach(el, v) {
> ...
> }
>
> Works for anything that supports iterators.

Ah, but what does the implementation of BOOST_FOREACH look like,
compared to the Lisp equivalent? Would it be something that would be
easy for most C++ programmers to produce?

jayessay

unread,
Sep 13, 2007, 2:10:16 PM9/13/07
to
Eli Bendersky <eli...@gmail.com> writes:

...

> Ruby is the latest and greatest in the evolution path of modern
> programming languages

I wonder how many people actually believe this...


> Leaving inefficiency apart for the moment[1], Ruby has anything Lisp

> has except macros. ... Again, I'll use Norvig's "seven points that
> make Lisp different".

These "seven points" miss some things. And at least one isn't even
true. A couple things missing from the seven points and from things
like Ruby:

* Condition system and what it can do.

* CLOS. In particular, the concept of generic functions (as
orthogonal to class structure) and multiple dispatch. Generic
functions are really what "OO" should be about.

* MOP.

Also the "Extensibility" point, as stated, is obviously false:
"everything is modifiable". Clearly the language itself isn't - for
example you can't change things like method dispatch. And you can't
add/change syntactic constructs. Sure, this is where you claim the
"uniform syntax" comes in, but really, that is an _enabler_ not the
actual point.


> [1] I see no reason why Ruby should be inherently less efficient than
> any other interpreted language.

Probably it can be reasonably compiled - but it isn't. And Lisp
systems have had large amounts of effort put into compilation
techniques for highly efficient code generation by extremely smart
folks for decades. It's hard to match that w/o comparable effort and
time frames...


/Jon

--
'j' - a n t h o n y at romeo/charley/november com

Emilio Lopes

unread,
Sep 13, 2007, 2:11:20 PM9/13/07
to
Raymond Wiker writes:

> Emilio Lopes <ec...@gmx.net> writes:

>> Here is my take: a simple macro called `debug', which prints a given

>> *expression* followed by its value. As a convenience it also returns
>> this same value.

>> [...]

> Not a convincing example of the power of (Lisp) macros; even C++ can
> do this:

> #include <iostream>
> #include <cmath>

> #define DEBUG(a) (std::cerr << "### " << #a << ": " << (a) << std::endl, a)

Does your macro work if the given expression has a side effect, like
in

int b=41;

float a = DEBUG(b++);

?

--
Emílio C. Lopes
Munich, Germany

Raymond Wiker

unread,
Sep 13, 2007, 2:46:08 PM9/13/07
to
Emilio Lopes <ec...@gmx.net> writes:

Nope... the value is evaluated twice - once for printout, and once
to yield a return value. The "traditional" way to solve this is to
setup a temporary variable to hold the value, which would be tricky to
do with a macro unless we want to restrict orselves to a single
datatype :-)

It might be possible to do this in an elegant manner with
templates, but I'm a bit fuzzy on the details of templates after to
years of not doing C++ at all (which would be absolutely wonderful if
I could use my preferred programming language instead :-)

Raymond Wiker

unread,
Sep 13, 2007, 2:54:06 PM9/13/07
to
Raymond Wiker <r...@RawMBP.local> writes:

> Nope... the value is evaluated twice - once for printout, and once
> to yield a return value. The "traditional" way to solve this is to
> setup a temporary variable to hold the value, which would be tricky to
> do with a macro unless we want to restrict orselves to a single

^ ourselves


> datatype :-)
>
> It might be possible to do this in an elegant manner with
> templates, but I'm a bit fuzzy on the details of templates after to

^ two

> years of not doing C++ at all (which would be absolutely wonderful if
> I could use my preferred programming language instead :-)

"I blame my keyboard."(tm)

Ken Tilton

unread,
Sep 13, 2007, 3:20:58 PM9/13/07
to

jayessay wrote:
> Eli Bendersky <eli...@gmail.com> writes:
>
> ...
>
>
>>Ruby is the latest and greatest in the evolution path of modern
>>programming languages

Yep, it actually leads Python on Google Fight, 97.8m to 79.8m. Wish I
had been keeping track, Python was way ahead not too long ago.

The bad news for Ruby is that it's reign will be just long enough for
O'Reilly to get stuck with a lot of Ruby inventory, Lisp is coming up
fast like a thorobred on a herd of donkeys at 25m.

The neat thing is that, as far as I can make out, Russell produced that
first working interpreter early in '59, with work on the implementation
having begun in late '58. We will be smack on top of the golden
anniversary when Lisp moves into first place to stay.

kt


--
http://www.theoryyalgebra.com/

"We are what we pretend to be." -Kurt Vonnegut

Rayiner Hashem

unread,
Sep 13, 2007, 4:21:26 PM9/13/07
to
> Ah, but what does the implementation of BOOST_FOREACH look like,
> compared to the Lisp equivalent? Would it be something that would be
> easy for most C++ programmers to produce?

Boost is a mix of C++ preprocessor code and template magic. As far as
I'm concerned, it's write-only code. In fact, almost every non-trivial
use of templates or the STL is very ugly on the library side. Eg:
extending Iterate to handle a new container is maybe a dozen lines of
code, while writing custom iterators for the STL involves a couple of
pages of code. Heck, I'd be quite surprised if anybody can write an
STL iterator from memory, without having to refer repeatedly to a
reference or some example code.

Ultimately, though, Boost, the STL, "Modern C++" is rooted in some
good ideas. It's good to have a generic iteration facility, to define
syntactic extensions to the language, to encapsulate design patterns
into meta-code. Lispers take these ideas for granted, but they were
basically unknown to C++ until Alexanderscu's book and the stuff that
followed it packaged the ideas into an appropriately well-hyped form.

Jeronimo Pellegrini

unread,
Sep 13, 2007, 4:32:08 PM9/13/07
to
On 2007-09-13, Thomas A. Russ <t...@sevak.isi.edu> wrote:
> Ah, but what does the implementation of BOOST_FOREACH look like,
> compared to the Lisp equivalent? Would it be something that would be
> easy for most C++ programmers to produce?

That's why I said it's easy to use C++ macros, but writing one would
drive you nuts... :-)

But I don't think the problem with C++ is that it's hard to iterate over
STL containers (it's simple with boost::foreach). There are other, more
fundamental problems with the language.

J.

Slobodan Blazeski

unread,
Sep 13, 2007, 4:46:24 PM9/13/07
to
> Eli- Hide quoted text -
>
Elis I found your post newbish. You want somebody else to show you a
great macro just to convince your friends that lisp is a great
language. And after doing a side by side, feature by feature
comparasion with Perl, Ruby and C# 3.0 you concluded that lisp has
nothing new to offer beside macros.

Actually the true is that people already show you great macros but
you're just unable to see them as such. It reminds me of a tv show
about Gregor Mendel, the father of genetics. It goes something like
this: Mendel shows his conclusions about experiments he done with peas
to some eminent professor and his assistent. The professor finally
says to his assistent awesome ,bravo, look what Mendel done with
peas, congratulate him. And the assistent says: But I don't eat peas.

What I like about lisp is very fast development , you get idea you try
it, fix it , there is no compiler to punch you in your nose everytime
you mispell or don't define some function , or call it with wrong
arguments, nor there are stupid scripts to load, change, reload WTF.
It's only you and the repl, you write and rewrite and stop only to do
thinking. The lisp implementation is your best friend not that boring
perfectionistic teacher that always fallows stupid rules and you're
forced to lie, cheat and use tricks only to have some fun. Lisp code
doesn't start with brilliant macros carved in stone, it's starts with
a lot of messy defuns, some classes, a lot of rewrites while you're
gaining knowledge of the probem you're trying to solve, than you're
starting to clean up the rats nest, remove unneeded globals, search
for patterns that first combine themselves in functions, and finally
when functions can't do the trick, the mighty macros took over,
Kaizen -continous change for the better, and finally there's stood
the remaining code. And it's a beatifull code, something that makes
you feel proud, and sparks the touch of creation, you feel
selffullfillment.
Eli
There's nothing special in lisp programs, the journey to write them is
magical.

After the problem is solved, you could rewrite it in different
languages, with different levels of succcess. Some of them will yield
code that's even shorter, or faster , or maybe even prettier. Question
is could you write that same code directly in you-name it language?
Well I coudn't.

Slobodan

John Thingstad

unread,
Sep 13, 2007, 5:37:58 PM9/13/07
to
PÃ¥ Thu, 13 Sep 2007 22:46:24 +0200, skrev Slobodan Blazeski
<slobodan...@gmail.com>:

>
> After the problem is solved, you could rewrite it in different
> languages, with different levels of succcess. Some of them will yield
> code that's even shorter, or faster , or maybe even prettier. Question
> is could you write that same code directly in you-name it language?
> Well I coudn't.

Well if you are a beginner you will probably spend most of your time
buried in the CLHS looking for a function or feature. It is less of a
problem now than when I started but still to some extent true. You need to
be somewhat fluent before any of this great productivity shows up. That is
why it is to difficult to convince new peole. Lisp is a language that
grows on you gradually. It doesn't immediately strike you neither as
beautiful or productive. At least that was true for me. My first Lisp
programs were clumsy. A form of C in Lisp. It has taken me years to
'think' Lisp (I still hit the wall some times). For this reason I am not
convinced a 'killer macro' is going to do it. Particularly it is highly
unlikely they will understand it. It will just look cryptic and confusing.

The best intro I have seen is the first chapter of Practical Common Lisp.
This is because you get into the mind of someone who knows the language
and see the interactive style. You see the program start with a few key
ideas and evolve into a mature form. Perhaps this is a better way to
introduce it?

I was going to stay out of this discussion, but I guess I couldn't help
myself.
Well.. Just my to cents.

Dimiter "malkia" Stanev

unread,
Sep 13, 2007, 5:44:07 PM9/13/07
to
Thanks for that! Nice example!

Aleksander Nabagło wrote:
> Pascal Costanza wrote:
>> Thomas F. Burdick wrote:
>>> On Sep 12, 10:28 am, Pascal Costanza <p...@p-cos.net> wrote:
>>>> Eli Bendersky wrote:
>>>>

> .....

Dimiter "malkia" Stanev

unread,
Sep 13, 2007, 5:49:05 PM9/13/07
to
Another cool macro! Thanks!
Message has been deleted

jayessay

unread,
Sep 13, 2007, 7:15:40 PM9/13/07
to
Ken Tilton <kenny...@optonline.net> writes:

> jayessay wrote:
> > Eli Bendersky <eli...@gmail.com> writes:
> > ...
> >
> >>Ruby is the latest and greatest in the evolution path of modern
> >>programming languages

Hey, I didn't write that nonsense.


> Yep, it actually leads Python on Google Fight, 97.8m to 79.8m. Wish I
> had been keeping track, Python was way ahead not too long ago.

And this has nothing to do with the (admitted value judgement) claim
that it is the "latest and greatest int the evolution [sic] path".
That is, Google fight is at best a "metric" (bad as it is) of
_popularity_; not "lateness" "greatness" or "evolutionary" aspects.


> The bad news for Ruby is that it's reign will be just long enough
> for O'Reilly to get stuck with a lot of Ruby inventory, Lisp is
> coming up fast like a thorobred on a herd of donkeys at 25m.

That's probably just a blip - their likely still making out very well
on their crappy Java books.


> The neat thing is that, as far as I can make out, Russell produced
> that first working interpreter early in '59, with work on the
> implementation having begun in late '58. We will be smack on top of
> the golden anniversary when Lisp moves into first place to stay.

Well, we can hope - but I wouldn't hold my breath ...

D Herring

unread,
Sep 13, 2007, 8:43:35 PM9/13/07
to
Eli Bendersky wrote:
> In short: I'm looking for a "killer" macro - a macro (or a couple of
> related ones) that show what Lisp can do and other languages (those
> without uniform syntax) can not.

Here's a simple example where code rewriting is almost necessary.

On the Boost developer's list, there was a recent (August 2007) debate
over a "scope exit" library. The idea is simple: intersperse
exception-handling code inside the function body, and only have it
execute if the function exits uncleanly.

Here's some C++ code from the author's website.
http://194.6.223.221/~nasonov/scope_exit-0.04/libs/scope_exit/doc/html/scope_exit/tutorial.html

void World::addPerson(Person const& person) {
bool commit = false;

m_persons.push_back(person);
BOOST_SCOPE_EXIT( (commit)(m_persons) )
{
if(!commit)
m_persons.pop_back();
} BOOST_SCOPE_EXIT_END

// ... other operations

commit = true;
}

Explanation: If "other operations" throw an exception, commit=true
will be skipped, and the clause in BOOST_SCOPE_EXIT will be triggered.

The problem is in delaying the evaluation of BOOST_SCOPE_EXIT's body
until the end of the function block. To do this in C++, an object is
created; it stores the cleanup code in its destructor. So the above
expands to something like

void World::addPerson(Person const& person) {
bool commit = false;

m_persons.push_back(person);
template <class T1, class T2>
class Guard {
Guard(T1 &x1, T2 &x2) : commit(x1), m_persons(x2)
~Guard() {
if(!commit)
m_persons.pop_back();
}
T1 &commit;
T2 &m_persons;
} guard_variable(commit, m_persons);

// ... other operations

commit = true;
}

Several uglinesses appear in this solution. Among the obvious:
- The variables to be used must be passed explicitely (C++ classes
nested inside functions cannot see the parent block's variables)
- In general, the variables must be passed "by reference"; an extended
syntax was proposed to allow something like
BOOST_SCOPE_EXIT( byref(commit)byval(m_persons) )
- An object is created and values are copied every time this runs --
even though the transformation is known at compile time. A good
enough compiler _might_ optimize that out.


With a decent macro processor (e.g. Lisp), the story is different.
(defmacro with-scope-guard (&body body)
"Implement ScopeExit in Lisp"
(let ((flag nil)
(normal (list 'progn))
(guards (list 'progn)))
(dolist (item body)
(cond (flag (push item guards)
(setf flag nil))
((eql item :scope-guard)
(setf flag t))
(t (push item normal))))
(list 'unwind-protect
(reverse normal)
(reverse guards))))

(defparameter *people* (list))
(defun guard-test (person)
"Demonstrate the ScopeExit implementation."
(let ((commit nil))
(with-scope-guard
(push person *people*)
:scope-guard (unless commit
(pop *people*))
; ... other operations
(setf commit t))))

Which macroexpands to something like
(DEFUN GUARD-TEST (PERSON)
(LET ((COMMIT NIL))
(UNWIND-PROTECT
(PROGN
(PUSH PERSON *PEOPLE*)
(SETF COMMIT T))
(PROGN
(UNLESS COMMIT
(POP *PEOPLE*))))))

Exactly what we want: no creating lists of variables which need to be
preserved, no question of how these variables should be passed, and
minimal runtime overhead. Plus, since Lisp offers unwind-protect,
there's no need to manually set the guard variable, commit.


Is that "killer"? It took me (a relative Lisp noob) all of 5 minutes
to write (less time than this email) -- well less that what has been
spent by the collective Boost gurus on their implementation.

- Daniel

Jason Sidabras

unread,
Sep 13, 2007, 9:11:03 PM9/13/07
to

This reply is beautiful and poetic. The passion behind LISP users is
amazing and one of the reasons that I am starting to learn LISP.

Rob Warnock

unread,
Sep 13, 2007, 11:33:47 PM9/13/07
to
D Herring <dher...@at.tentpost.dot.com> wrote:
+---------------

| (defun guard-test (person)
| "Demonstrate the ScopeExit implementation."
| (let ((commit nil))
| (with-scope-guard
| (push person *people*)
| :scope-guard (unless commit
| (pop *people*))
| ; ... other operations
| (setf commit t))))
|
| Which macroexpands to something like
| (DEFUN GUARD-TEST (PERSON)
| (LET ((COMMIT NIL))
| (UNWIND-PROTECT
| (PROGN
| (PUSH PERSON *PEOPLE*)
... ...[you left out "other operations" here]...

| (SETF COMMIT T))
| (PROGN
| (UNLESS COMMIT
| (POP *PEOPLE*))))))
|
| Exactly what we want: no creating lists of variables which need to be
| preserved, no question of how these variables should be passed, and
| minimal runtime overhead. Plus, since Lisp offers unwind-protect,
| there's no need to manually set the guard variable, commit.
|
| Is that "killer"? It took me (a relative Lisp noob) all of 5 minutes
| to write (less time than this email) -- well less that what has been
| spent by the collective Boost gurus on their implementation.
+---------------

While this is true -- and congratulations, by the way, nice job! --
I confess that it *doesn't* really seem to meet the bar for a
"killer macro" to me, given that CL already has UNWIND-PROTECT
in the first place. The raw macroexpanded version is just as easy
to write manually than the WITH-SCOPE-GUARD version. (Sorry!)

Slightly easier, actually, since you really don't need the
second PROGN, given the syntax of UNWIND-PROTECT.


-Rob

-----
Rob Warnock <rp...@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607

D Herring

unread,
Sep 14, 2007, 12:40:56 AM9/14/07
to

No problem.

The point of ScopeExit is to localize the cleanup code with what's
being cleaned up, and delay evaluation until exit from the entire
function. This trivial example doesn't show any benefit -- a 100-line
function body containing several ScopeExits might be another story.
In that case, a raw unwind-protect might put 50 lines between the
action and its conditional cleanup.

This is a real-world example where a short Lisp macro easily solves a
difficult C++ problem. The corresponding C++ code is much longer and
harder to follow. Whether this qualifies as "killer" doesn't really
matter much (I already have a language preference).


> Slightly easier, actually, since you really don't need the
> second PROGN, given the syntax of UNWIND-PROTECT.

The second progn is there to handle multiple :scope-guard clauses.
What's missing is a gensym to auto-generate the "complete" flag that
conditions the execution of :scope-cleanup clauses.

- Daniel

D Herring

unread,
Sep 14, 2007, 12:51:34 AM9/14/07
to
D Herring wrote:
> Rob Warnock wrote:
>> D Herring <dher...@at.tentpost.dot.com> wrote:
>> +---------------
>> | (defun guard-test (person)
>> | "Demonstrate the ScopeExit implementation."
>> | (let ((commit nil))
>> | (with-scope-guard
>> | (push person *people*)
>> | :scope-guard (unless commit
>> | (pop *people*))
>> | ; ... other operations
>> | (setf commit t))))
>> | | Which macroexpands to something like
>> | (DEFUN GUARD-TEST (PERSON)
>> | (LET ((COMMIT NIL))
>> | (UNWIND-PROTECT
>> | (PROGN
>> | (PUSH PERSON *PEOPLE*)
>> ... ...[you left out "other operations" here]...
>> | (SETF COMMIT T))
>> | (PROGN
>> | (UNLESS COMMIT
>> | (POP *PEOPLE*))))))
>> +---------------
> > ... you really don't need the

> > second PROGN, given the syntax of UNWIND-PROTECT.
>
> The second progn is there to handle multiple :scope-guard clauses.

Drrb. Noob-alert.

CLHS syntax:
(unwind-protect protected-form cleanup-form*) => result*

Now I get it.

Thanks,
Daniel

Rob Warnock

unread,
Sep 14, 2007, 1:01:03 AM9/14/07
to
D Herring <dher...@at.tentpost.dot.com> wrote:
+---------------
| Rob Warnock wrote:
| > ...[elided]...

|
| The point of ScopeExit is to localize the cleanup code with what's
| being cleaned up, and delay evaluation until exit from the entire
| function. This trivial example doesn't show any benefit -- a 100-line
| function body containing several ScopeExits might be another story.
| In that case, a raw unwind-protect might put 50 lines between the
| action and its conditional cleanup.
+---------------

Aha! yes, I'd overlooked that part of it. You're correct.

+---------------


| > Slightly easier, actually, since you really don't need the
| > second PROGN, given the syntax of UNWIND-PROTECT.
|
| The second progn is there to handle multiple :scope-guard clauses.

+---------------

My point was just that UNWIND-PROTECT already accepts multiple
forms in the cleanup section, so the second PROGN is redundant.
You can just ",@" the list of cleanup forms directly into the
UNWIND-PROTECT being generated.

+---------------


| What's missing is a gensym to auto-generate the "complete" flag
| that conditions the execution of :scope-cleanup clauses.

+---------------

What would be useful is to be able to name each of the ScopeExits,
and then MACROLET a COMMIT macro that takes a name that maps to
the correct LET-bound GENSYM'd boolean. That is, something like:

(with-scope-exits
...stuff...
(scope-exit (foo)
...stuff-to-do-for-foo-chunk...
...rollback-forms-for-foo-chunk...)
...more-stuff...
(commit foo)
...even-more-stuff...)

So at the beginning WITH-SCOPE-EXITS would "(LET ((#:G123 nil) ...) ..."
[where #:G123 is the GENSYM the macro assigned to FOO], and then
the (COMMIT FOO) would expand to (SETF #:G123 T), and then one
of the cleanup forms for the UNWIND-PROTECT would be:

(unless #:G123
...rollback-forms-for-foo-chunk...)

Is that what you're talking about?

Ken Tilton

unread,
Sep 14, 2007, 1:21:09 AM9/14/07
to

Jason Sidabras wrote:
>>Elis I found your post newbish. You want somebody else to show you a
>>great macro just to convince your friends that lisp is a great
>>language. And after doing a side by side, feature by feature
>>comparasion with Perl, Ruby and C# 3.0 you concluded that lisp has
>>nothing new to offer beside macros.
>>
>>Actually the true is that people already show you great macros but
>>you're just unable to see them as such. It reminds me of a tv show
>>about Gregor Mendel, the father of genetics. It goes something like
>>this: Mendel shows his conclusions about experiments he done with peas
>>to some eminent professor and his assistent. The professor finally
>>says to his assistent awesome ,bravo, look what Mendel done with
>>peas, congratulate him. And the assistent says: But I don't eat peas.
>>
>>What I like about lisp is very fast development , you get idea you try
>>it, fix it , there is no compiler to punch you in your nose everytime
>>you mispell or don't define some function , or call it with wrong
>>arguments,

<cough> What lisp is Slobby using? Anyway...

Yes, at long last there is someone to whom I can pass The Torch. A
Farewell Tour of local Lisp groups is in the works. Slobby you'll find
the key to the kennel under your mat.

The Kenny is dead! Long live The Slobby!

Eli Bendersky

unread,
Sep 14, 2007, 2:12:31 AM9/14/07
to
> > Ruby is the latest and greatest in the evolution path of modern
> > programming languages
>
> I wonder how many people actually believe this...

Unfortunately for Lisp, about 100 times as much as the total amount of
CL users.

>
> > Leaving inefficiency apart for the moment[1], Ruby has anything Lisp
> > has except macros. ... Again, I'll use Norvig's "seven points that
> > make Lisp different".
>
> These "seven points" miss some things. And at least one isn't even
> true. A couple things missing from the seven points and from things
> like Ruby:
>
> * Condition system and what it can do.
>

What can it do, indeed ? This is meant as an innocent question. Can
you explain in a couple of sentences how CL's condition system is
better than Ruby's exception handling ?

> * CLOS. In particular, the concept of generic functions (as
> orthogonal to class structure) and multiple dispatch. Generic
> functions are really what "OO" should be about.
>

Yes, and on the other hand many known Lispers (PG, for example) claim
that Lisp is "above" OO.

> * MOP.

Hmm... I think Ruby has this.

Eli

Pascal Costanza

unread,
Sep 14, 2007, 2:34:47 AM9/14/07
to

No, not really.

When do you start to form your own opinions?


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/

Rainer Joswig

unread,
Sep 14, 2007, 2:55:50 AM9/14/07
to
In article <1189750351.0...@r29g2000hsg.googlegroups.com>,
Eli Bendersky <eli...@gmail.com> wrote:

> > > Ruby is the latest and greatest in the evolution path of modern
> > > programming languages
> >
> > I wonder how many people actually believe this...
>
> Unfortunately for Lisp, about 100 times as much as the total amount of
> CL users.
>
> >
> > > Leaving inefficiency apart for the moment[1], Ruby has anything Lisp
> > > has except macros. ... Again, I'll use Norvig's "seven points that
> > > make Lisp different".
> >
> > These "seven points" miss some things. And at least one isn't even
> > true. A couple things missing from the seven points and from things
> > like Ruby:
> >
> > * Condition system and what it can do.
> >
>
> What can it do, indeed ? This is meant as an innocent question. Can
> you explain in a couple of sentences how CL's condition system is
> better than Ruby's exception handling ?

Common Lisp has a model where on error there is
* a look-up for a matching handler
* the handler found is being called
* the handler can look for restarts and pick one

Note that the handler is called. In many other exception systems
there is a 'jump' to the handler and this is unwinding the stack.
In CL this means one can for example fix the error and just
continue at the exact place.

For example see this usage of ASSERT. ASSERT checks the first form.
If it fails you can reset the list of places we have mentioned
and continue.
The check will be done again. Until we have the form returning
non-nil.

Welcome to OpenMCL Version 1.0-rc1-050922 (DarwinPPC32)!
? (defun foo (a)
; let's check that a is odd
(assert (oddp a) (a) "A=~a is even, make it odd" a)
; we reach here only if a is really odd
(+ a 1))
FOO
? (foo 2)
> Error in process listener(1): A=2 is even, make it odd
> While executing: CCL::%ASSERTION-FAILURE
> Type :GO to continue, :POP to abort.
> If continued: allow some places to be set and test the assertion again.
Type :? for other options.
1 > (continue)
Type expressions to set places to, or nothing to leave them alone.
Value for A: 2
> Error in process listener(1): A=2 is even, make it odd
> While executing: CCL::%ASSERTION-FAILURE
> Type :GO to continue, :POP to abort.
> If continued: allow some places to be set and test the assertion again.
Type :? for other options.
1 > (continue)
Type expressions to set places to, or nothing to leave them alone.
Value for A: 3
4

There are many others uses of resumable exceptions.

>
> > * CLOS. In particular, the concept of generic functions (as
> > orthogonal to class structure) and multiple dispatch. Generic
> > functions are really what "OO" should be about.
> >
>
> Yes, and on the other hand many known Lispers (PG, for example) claim
> that Lisp is "above" OO.
>
> > * MOP.
>
> Hmm... I think Ruby has this.

Partly.

http://lispm.dyndns.org/documentation/amop/toc.html

>
> Eli

Pascal Costanza

unread,
Sep 14, 2007, 3:12:51 AM9/14/07
to
Thomas F. Burdick wrote:
>> The two examples in that blog entry crystallize what I personally think
>> is the essence of macros. I also noticed that many people don't get the
>> second example though. It'd be nice to see another example that's
>> equally compact and conveys the same idea.
>
> The big problem I see with those examples is that what they're trying
> to use structural transformation as a lever to do is ... hide
> lambdas. I'm not really au courant of the most recent scripting
> languages, but I have the impression that they're on an asymptotic
> course not so much with Lisp as with Smalltalk. So aiming one's
> arguments at ST'ers might be overshooting, but that's okay.
>
> For the problem of wanting to write your own "do while except return
> when" control structure (to pick a silly example), there's a perfectly
> good non-macro solution.

Sure. For example, you could implement them in assembly language.

> In ST, not only are anonymous functions
> hilariously light-weight from a syntactic point of view, but the
> *entire* language uses a dead-simple evaluation model: the receiving
> expression is evaluated, then the arguments to pass with the message,
> then the message is passed. Always, that's it. The language itself
> doesn't really have special forms, they're all implemented as methods
[...]

Of course, Smalltalk has special forms as well. For example, assignment,
message selectors, message sending itself, etc.

Any language needs a set of core operators that are not derived from
anything else.

> -- which means that *all* divergences from the normal order of
> evaluation use blocks, whether they be the super-banal ifTrue:ifFalse:
> or my wacky doWhileTrue:exceptReturnWhen: [1]. This creates a
> language ecology where a while/if/do-while-true-except-return-when
> that works in a functional style does not in fact leak abstractions.
> "This changes the order of evaluation" is part of the interface, not
> the implementation.

Of course, this leaks abstractions.

Assume you implement a functional version of if:

(defvar *true* (lambda (true false) (funcall true)))
(defvar *false* (lambda (true false) (funcall false)))

(defun f-if (pred then else)
(funcall (funcall pred) then else))

(f-if (lambda () (if (< 1 10) *true* *false*))
(lambda () (print 'less))
(lambda () (print 'more)))

Now you realize that the first argument can be eagerly evaluated. You
have to change your interface:

(defun f-if-2 (pred then else)
(funcall pred then else))

(f-if-2 (if (< 1 10) *true* *false*)
(lambda () (print 'less))
(lambda () (print 'more)))

Whenever you have to change the interface because you want to change a
mere implementation detail that doesn't add to the abstraction as such,
your abstraction is leaking.

Pascal Costanza

unread,
Sep 14, 2007, 3:29:24 AM9/14/07
to
Eli Bendersky wrote:
> On Sep 12, 5:54 pm, Brian Adkins <lojicdot...@gmail.com> wrote:

>> On Sep 12, 9:04 am, Pascal Costanza <p...@p-cos.net> wrote:
>>
>>> Eli Bendersky wrote:
>>>> What, specifically, can't you do ? with-open-file ? while ?
>>> While without lambdas (or blocks, or whatever you want to call them).
>>> Pascal
>> I think Eli was focused on the first example in the article (possibly
>> because it was the first example) which is easy to do in Ruby with a
>> block:
>>
>> File.open(filename) {|in| do_something(in) }
>>
>> I think you're right about not being able to do while w/o lambdas, but
>> it's not too bad (syntactically) with them. The efficiency is worse
>> than macros, but then Ruby is dog slow anyway:
>>
>> def while_function pred, &b
>> if pred.call
>> yield
>> while_function(pred, &b)
>> end
>> end
>>
>> n = 3
>> while_function(lambda { n > 0 }) do
>> puts n
>> n -=1
>> end
>>
>> Having notational convenience via macros plus efficiency is a big win
>> with Lisp. From my perspective as a Ruby programmer learning Lisp, the
>> thought of having a more powerful language *and* order(s) of magnitude
>> faster execution felt like having my cake and eating it too.
>
> Thanks for this, it is exactly what I intended to write. These
> examples just show something that's slightly more elegant in Lisp than
> in Ruby. Hardly a reason for all Lispers to brag how superior their
> language is.
>
> Unfortunately, 40+ posts into this thread, there still is no real
> answer.

It's important to step two or three steps back at this stage:

No language is objectively "better" than any other. As long as a
language is Turing complete and has good access to the capabilities of
the underlying operating system, you can do whatever you want in it.
Turing equivalence means that whenever you can express something in one
language, you can also express it in another.

The _only_ differences between programming languages that matter are
those of convenience, and convenience is a highly subjective matter.
Some people prefer to exclusively think in a functional style, some
people prefer to exclusively think in an object-oriented style, some
people only feel comfortable if a language allows them to formally prove
certain aspects of their programs, some people want a large library so
they don't have to program a lot themselves, some people prefer a
minimal set of language constructs because they are bad at remembering
more, some people prefer to mix and match paradigms as they go, some
people prefer to remain pure, and so on, and so on, ad infinitum.

These are all trade offs, and there is no single best choice that suits
everyone in all contexts one can think of. At some stage, there is no
other way than to just make a decision and go with one language, or a
couple of them, based on personal preferences, and stick to that
decision at least for a while.

This doesn't mean that you cannot make objective statements about
certain language constructs. And that's the _only_ thing I wanted to
make in my blog posting about what I think is the point of macros.

It's an objective truth that you can create syntactic abstractions with
macros, and that they allow you to hide implementation details that you
cannot hide in any other way. Functional abstractions are leaky in
certain regards which can only be fixed by macros in the general case.
Yes, there are always ways to provide workarounds in one or the other
language (lazy evaluation, light-weight syntax for lambda expressions,
etc.), but none of them does this in such a fundamental way as macros
do. [1]

I couldn't care less whether you think that this buys you a lot or not.
That's a subjective value judgement, and I don't know enough about your
personal preferences to make any claims about them. I can only state
that I know that the objective increase in expressiveness that macros
provide is important _to me_ from the perspective of _my_ personal
preferences, and I can inform you about this. Afterwards, you have to
make your own choice.


Pascal

[1] Except for fexprs or nlambdas in some Lisp dialects, but that's a
different topic.

Rainer Joswig

unread,
Sep 14, 2007, 4:42:02 AM9/14/07
to
In article <5kurijF...@mid.individual.net>,
Pascal Costanza <p...@p-cos.net> wrote:

This need to be 'superior' or 'better' is bullshit. A programming language
is not just a bunch of technical features with 'objective'
criteria to rank them.

Programmer's needs and capabilities are vastly different.
Programming languages are like a social eco-system. Plus
there is human behavior engraved into our brains. For example
people have the tendency to like what they already know. There
is a hurdle to unlearn or relearn.

I simply doubt we
can judge whether Lisp is useful to somebody because we
saw a cute macro example in some newsgroup. Macros might
even be the wrong thing for the typical Ruby user.
It might change the language in a way that makes it
too complicated for many users.

If languages like Ruby or Python appeal to many people,
that is perfectly fine. They have a large community,
share lots of stuff, are friendly within their communities
and so on. Technically there are a few differences, but
mostly they provide a kind of object-oriented programming
experience with some 'scripting' built-in.

Now people from the outside look at Lisp. It is still there
after almost 50 years. It must be superior. Or not? Then one
hears vague claims that it is better and some propaganda.
Now people here in comp.lang.lisp are questioned over and
over, why Lisp is better than X, Y, Z. For what definition
of better I have to ask? If Ruby has the library and
the programming model that you need and understand, then there
is a hurdle to overcome to understand Lisp, find a library
or maybe create a library yourself? Is the learning effort
worth it? Is Lisp so much 'better' that the effort to learn
it is well spend. Hard to say.

From my side, I also look at, say, Ruby. Then I think is it
worth it to learn it or even to use it? Would it pay
back? Much what I know from Lisp applies directly
to programming with Ruby. There is little advantage to switch.
Still it is useful to understand where things are heading
in terms of libraries, usability, infrastructure and so on.


But coming back to macros. There are some programming styles
in Lisp where you see lots of macros. Many examples
can be found in Paul Graham's book 'On Lisp'
(available as PDF here: http://lib.store.yahoo.net/lib/paulgraham/onlisp.pdf ).
It covers many/most uses of macros.

Then is some software (in this case CL-HTTP) you see a snippet of code like this:


(defmethod copy-file (from-pathname to-pathname &key (copy-mode :text) (copy-creation-date t) report-stream &allow-other-keys)
(with-copy-file-environment (from-pathname to-pathname report-stream)
(let ((element-type (copy-mode-element-type copy-mode))
modification-date creation-date author)
(with-open-file (from from-pathname :direction :input :element-type element-type :if-does-not-exist :error)
(with-open-file (to to-pathname :direction :output :element-type element-type :if-exists :supersede
:if-does-not-exist :create)
(stream-copy-until-eof from to copy-mode))
(when copy-creation-date
(cond-every
((setq modification-date (file-stream-modification-date from))
(setf (file-modification-date to-pathname) modification-date))
((setq creation-date (file-stream-creation-date from))
(setf (file-creation-date to-pathname) creation-date)))))
(when (and copy-creation-date (setq author (file-author from-pathname)))
(set-file-author to-pathname author nil))
to-pathname)))

Macros are DEFMETHOD, WITH-COPY-FILE-ENVIRONMENT, WITH-OPEN-FILE, COND-EVERY, SETF, WHEN.

So we have built-in macros DEFMETHOD, WITH-OPEN-FILE, SETF and WHEN.
The author added WITH-COPY-FILE-ENVIRONMENT and COND-EVERY.
COND-EVERY is just a version of COND that checks every clause.
It expands to some efficient low-level form without the need to introduce
anonymous functions (or BLOCKs in Ruby).

WITH-COPY-FILE-ENVIRONMENT is one of those WITH- macros that set a scope and inside the scope
there is some effect.

In this case it hides the handling of errors and providing of restarts:

(defmacro with-copy-file-environment ((from to stream &optional (block-name 'copy-file))
&body body)
`(tagbody
retry
(when ,stream
(format ,stream "~&Copying ~A to ~A . . ." (name-string ,from) (name-string ,to)))
(restart-case
(return-from ,block-name
(multiple-value-prog1 (progn ,@body)
(when ,stream
(format ,stream "~&Copied ~A to ~A." (name-string ,from)
(name-string ,to)))))
(retry ()
:report (lambda (stream)
(format stream "Retry copying ~A" (name-string ,from)))
(go retry))
(skip ()
:report (lambda (stream)
(format stream "Skip copying ~A" (name-string ,from)))
(when ,stream
(format ,stream "~A not copied." (name-string ,from)))
(return-from ,block-name nil)))))

Above is one of the examples where a template like approach to code generation is sufficient.
The macro completely decouples the complex handling logic from the code in COPY-FILE.
The macro provides RESTARTs for RETRY and SKIP. Whenever there is an error during copying
a file to another, the user will get these two restarts.

The USER code in COPY-FILE is uncluttered. The complex stuff is reusable in the macro.

Let's check a simple use:

(let ((from (pathname "/tmp/test10.lisp"))
(to (pathname "/tmp/test11.lisp")))
(with-copy-file-environment (from to *standard-output*)
(error "just an example")))

SLIME shows me the following display in the debugger. Note that our new RESTARTs are available.

Execution of a form compiled with errors:
(RETURN-FROM COPY-FILE
(MULTIPLE-VALUE-PROG1
(PROGN
(ERROR "just an example"))
(WHEN *STANDARD-OUTPUT*
(FORMAT *STANDARD-OUTPUT* "~&Copied ~A to ~A." # #))))
[Condition of type KERNEL:SIMPLE-PROGRAM-ERROR]

Restarts:
0: [RETRY] Retry copying /tmp/test10.lisp
1: [SKIP] Skip copying /tmp/test10.lisp
2: [ABORT] Return to SLIME's top level.
3: [ABORT] Return to Top-Level.

Backtrace:
0: ("Top-Level Form")[:TOP-LEVEL]
1: ("Top-Level Form")[:TOP-LEVEL]
2: ("Top-Level Form")[:TOP-LEVEL]
--more--


If you use, say, CMUCL without Emacs and Slime, the it looks like this:

* (let ((from (pathname "/tmp/test10.lisp"))
(to (pathname "/tmp/test11.lisp")))
(with-copy-file-environment (from to *standard-output*)
(error "just an example")))

Execution of a form compiled with errors:
(RETURN-FROM COPY-FILE
(MULTIPLE-VALUE-PROG1
(PROGN
(ERROR "just an example"))
(WHEN *STANDARD-OUTPUT*
(FORMAT *STANDARD-OUTPUT*
"~&Copied ~A to ~A."
(NAME-STRING FROM)
(NAME-STRING TO)))))
[Condition of type KERNEL:SIMPLE-PROGRAM-ERROR]

Restarts:
0: [RETRY] Retry copying /tmp/test10.lisp
1: [SKIP ] Skip copying /tmp/test10.lisp
2: [ABORT] Return to Top-Level.

Debug (type H for help)

("Top-Level Form")[:TOP-LEVEL]
Source: (WITH-COPY-FILE-ENVIRONMENT (FROM TO *STANDARD-OUTPUT*)
(ERROR "just an example"))
0]


We see in the terminal that our RESTARTs are there. One Restart which was added by SLIME is no longer there,
since we don't run it with SLIME in this example.


He, you got a few things in one example:

* how you can extract complex control flow code with a macro from user code

* how a macro looks like that uses a template like code generation

* how the CONDITION system lets you add user visible RESTARTs.

Isn't that nice.

--
http://lispm.dyndns.org

Pascal Costanza

unread,
Sep 14, 2007, 6:24:56 AM9/14/07
to
philip....@gmail.com wrote:

> On Sep 12, 9:28 am, Pascal Costanza <p...@p-cos.net> wrote:
>> Eli Bendersky wrote:
>>> In short: I'm looking for a "killer" macro - a macro (or a
>>> Any suggestions for such an example ?
>> Here is my best attempt so far:http://p-cos.blogspot.com/2007/02/what-is-point-of-macros.html
>
> Over the last few months I've shown Pascal's example to a few of my
> skeptical colleagues and have had a reasonable success rate (no actual
> converts but certainly some curiosity). I think the examples are good
> because they're short and in my experience, when advocating a
> programming language, you have about 60 seconds to hook someone before
> they return to Eclipse.
>
> I suspect these examples work best on Java and C# programmers as these
> languages provide the least support for this way of thinking.

That's no surprise, because I come from a Java background.

> The more
> dynamic languages all have their own approximations which make the
> examples seem less impressive. But perhaps Ruby, Python and Smalltalk
> programmers can better appreciate a longer DSL-style example.

A couple of months ago, I had a discussion with Robert Hirschfeld, who I
collaborate with on context-oriented programming. While I am working on
ContextL for Common Lisp, he implements something very similar for
Smalltalk/Squeak.

I was a little bit puzzled by his design. While in ContextL, you can say
something like this:

(with-active-layers (layer1 layer2)
... some code ...)

In ContextS, you have to say the same thing as follows:

[ {Layer1 new, Layer2 new} ]
useAsLayersFor: [ ... some code ...]

While I understand that it's natural in Smalltalk to use blocks for
delaying evaluation of code, I wondered why he puts the layers first and
uses a relatively weird name for layer activation, instead of putting
something like withActiveLayers in front and passing two arguments to it.

What I hadn't realized: In Smalltalk, it's actually impossible to add
any new constructs other than by declaring new methods. Such new methods
always require a message send to some object. There is no way to add
plain function calls, lest to speak any macros. There are also no
implicit self sends with which you could fake this.

I guess, I would try to come up with an example where I add a new
language construct that doesn't require a target object for a message
send, if I were to try to illustrate the benefits of macros to a
Smalltalker. (But then again, plain functions would already be
beneficial here...)


Pascal

Pascal Costanza

unread,
Sep 14, 2007, 6:26:37 AM9/14/07
to
Barry Margolin wrote:
> In article <5kq52mF...@mid.individual.net>,
> Pascal Costanza <p...@p-cos.net> wrote:
>
>> alkond...@gmail.com wrote:
>>> I think SETF facility is the best example. You can build your own
>>> magic on top of it (like ! macro from "On Lisp"). E.g. some other
>>> languages have concept of reference, but few support generic places.
>>> This is something them I'm missing in other languages (except C++
>>> where it's possible to do similar things with it's templates black
>>> magic).
>> Same problem as with my while macro - or probably even worse. Many
>> people don't see why (setf (car cons) 345) is better than (set-car! cons
>> 345).
>
> Yet they probably have little problem understanding why conventional
> languages use the same assignment syntax for everything:
>
> var = val;
> array[subscript] = val;
> struct.member = val;
>
> They would presumably consider it cumbersome to have to write:
>
> array_assign(array, subscript, val);

Yes, this is also a complete mystery to me. Even if you put the
equivalent code using Common Lisp's setf and the other language's
assignment operator side by side, they don't seem to get it.

That's weird, I don't have a good explanation for that.

Pascal Costanza

unread,
Sep 14, 2007, 6:31:30 AM9/14/07
to
Rainer Joswig wrote:
> This need to be 'superior' or 'better' is bullshit. A programming language
> is not just a bunch of technical features with 'objective'
> criteria to rank them.
>
> Programmer's needs and capabilities are vastly different.
> Programming languages are like a social eco-system. Plus
> there is human behavior engraved into our brains. For example
> people have the tendency to like what they already know. There
> is a hurdle to unlearn or relearn.

I totally agree.

> I simply doubt we
> can judge whether Lisp is useful to somebody because we
> saw a cute macro example in some newsgroup.

Especially because macros - like any other metaprogramming approach -
only really pay off in large and complex systems, where it is worthwhile
to condense involved and longish code patterns into smaller units.


Pascal

Slobodan Blazeski

unread,
Sep 14, 2007, 7:23:43 AM9/14/07
to
Question: How does a large software project get to be one year late?
Answer: One day at a time!
Fred Brooks-The Mythical Man-Month

Metaprogramming pays very well in medium size projects also , beside
condesing code, there is the utility syndrom. If you write something
usefull you tend to use it, again and again and again.
While ago I wrote a simple one liner called string+, and now I lost
count how many times I use it. It's contagious.

Pascal Costanza

unread,
Sep 14, 2007, 7:54:30 AM9/14/07
to

Yes, I forgot about better reuse of code patterns, which is also useful
in small and medium-sized projects. Thanks for mentioning it.

Ken Tilton

unread,
Sep 14, 2007, 8:14:50 AM9/14/07
to

Eli Bendersky wrote:
>>>Ruby is the latest and greatest in the evolution path of modern
>>>programming languages
>>
>>I wonder how many people actually believe this...
>
>
> Unfortunately for Lisp, about 100 times as much as the total amount of
> CL users.

Unfortunate? Oh yes. I swung by the monastery the other day and the
monks were crying in their twig teas about all the people down at the
football stadium. I suggested tailgate parties, wet T-shirt contests,
and Monday Night Sitting. They liked it, we're talking to NPR about
broadcast rights.

kenny

--
http://www.theoryyalgebra.com/

"We are what we pretend to be." -Kurt Vonnegut

pineapp...@yahoo.com

unread,
Sep 14, 2007, 9:47:22 AM9/14/07
to
> > Works for anything that supports iterators.
>
> Ah, but what does the implementation of BOOST_FOREACH look like,
> compared to the Lisp equivalent? Would it be something that would be
> easy for most C++ programmers to produce?

I couldn't get anything like what this poster posted for a C++
iterator to work. My simple problem required something far more
monstrous and ugly.

Wade Humeniuk

unread,
Sep 14, 2007, 10:06:18 AM9/14/07
to

MY real answer, is ... (BTW, Killer?? as in????)

1) Macros can be fun, I want programming to be enjoyable.

2) I can do things like create a macro at the heart of a a c++
lexer that looks like

(in-package :archecode.c++)

;; The core lexer for C++, will break up C++ code into a sequence
;; of tokens. (complete with token start position, line and character)
(defun top-level-lexer (stream)
(handler-case
(lexer-loop (c stream top-level-lexer) ;; Here is the macro
(member->loop (#\space #\tab #\newline #\cr #\ff))
(c->loop #\# (lexdirective stream))
(c->loop #\/ (lexcomment stream))
(p->token start-symbol-char-p (lexback c stream) (lexsymbol stream))
(backtrack-if-not (#\. digit-char-p) (lexnumber stream))
(p->token digit-char-p (lexback c stream) (lexnumber stream))
(c->token #\()
(c->token #\))
(c->token #\[)
(c->token #\])
(c->token #\{)
(c->token #\})
(c->token #\;)
(c->token #\,)
(c->token #\.)
(c->token #\~)
(ctree->token (#\: #\:))
(ctree->token (#\+ #\+ #\=))
(ctree->token (#\- #\- #\= #\>))
(ctree->token (#\* #\=))
(ctree->token (#\/ #\=))
(ctree->token (#\% #\=))
(ctree->token (#\> (#\> #\=) #\=))
(ctree->token (#\< (#\< #\=) #\=))
(ctree->token (#\& #\& #\=))
(ctree->token (#\| #\| #\=))

ARCHECODE.C++ 9 > (with-input-from-string
(s "class test {
public:
test() {}
virtual ~test();
private:
int i;
void func1(int,float);
};")
(pprint (lex s)))

((|class| NIL 0 5)
(%SYMBOL "test" 0 10)
({ NIL 0 12)
(|public| NIL 1 45)
(\: NIL 1 46)
(%SYMBOL "test" 2 80)
(\( NIL 2 81)
(\) NIL 2 82)
({ NIL 2 84)
(} NIL 2 85)
(|virtual| NIL 3 122)
(~ NIL 3 124)
(%SYMBOL "test" 3 128)
(\( NIL 3 129)
(\) NIL 3 130)
(\; NIL 3 131)
(|private| NIL 4 165)
(\: NIL 4 166)
(|int| NIL 5 199)
(%SYMBOL "i" 5 201)
(\; NIL 5 202)
(|void| NIL 6 236)
(%SYMBOL "func1" 6 242)
(\( NIL 6 243)
(|int| NIL 6 246)
(\, NIL 6 247)
(|float| NIL 6 252)
(\) NIL 6 253)
(\; NIL 6 254)
(} NIL 7 282)
(\; NIL 7 283))

jayessay

unread,
Sep 14, 2007, 11:26:20 AM9/14/07
to
Eli Bendersky <eli...@gmail.com> writes:

> > > Ruby is the latest and greatest in the evolution path of modern
> > > programming languages
> >
> > I wonder how many people actually believe this...
>
> Unfortunately for Lisp, about 100 times as much as the total amount of
> CL users.

Why is it "unfortunate"? Doesn't seem to matter at all one way or the
other.


> > * Condition system and what it can do.
> >
>
> What can it do, indeed ? This is meant as an innocent question. Can
> you explain in a couple of sentences how CL's condition system is
> better than Ruby's exception handling ?

I don't spend much time on this sort of "task". Here's one sentence,
which you should then use as a prod to check it out further:
Exceptions are a proper subset of conditions.


> > * CLOS. In particular, the concept of generic functions (as
> > orthogonal to class structure) and multiple dispatch. Generic
> > functions are really what "OO" should be about.
> >
>
> Yes, and on the other hand many known Lispers (PG, for example) claim
> that Lisp is "above" OO.

Irrelevant. The point stands. And BTW, PG doesn't make this claim,
he merely claims OO is ill defined and highly overrated.

> > * MOP.
>
> Hmm... I think Ruby has this.

You should try to think harder. If you accept the CLOS claim, you
should realize that the MOP claim is significantly stronger.

jayessay

unread,
Sep 14, 2007, 11:29:43 AM9/14/07
to
Ken Tilton <kenny...@optonline.net> writes:

> Eli Bendersky wrote:
> >>>Ruby is the latest and greatest in the evolution path of modern
> >>>programming languages
> >>
> >>I wonder how many people actually believe this...
> > Unfortunately for Lisp, about 100 times as much as the total amount
> > of
> > CL users.
>
> Unfortunate? Oh yes. I swung by the monastery the other day and the
> monks were crying in their twig teas about all the people down at the
> football stadium. I suggested tailgate parties, wet T-shirt contests,
> and Monday Night Sitting. They liked it, we're talking to NPR about
> broadcast rights.

This is _vastly_ better than my reply on this comment. I guess that's
why you are "Your Kennyness"...

Pascal Costanza

unread,
Sep 14, 2007, 11:48:26 AM9/14/07
to
Eli Bendersky wrote:

> Leaving inefficiency apart for the moment[1], Ruby has anything Lisp

> has except macros. Ruby is the latest and greatest in the evolution
> path of modern programming languages and their convergence path with
> Lisp.

So you're basically saying that Ruby is a very tall midget. ;)

Chris Russell

unread,
Sep 14, 2007, 12:45:27 PM9/14/07
to
On 13 Sep, 15:30, Eli Bendersky <eli...@gmail.com> wrote:

> Leaving inefficiency apart for the moment[1], Ruby has anything Lisp
> has except macros. Ruby is the latest and greatest in the evolution
> path of modern programming languages and their convergence path with

> Lisp. Again, I'll use Norvig's "seven points that make Lisp
> different".
>
If you want something cool that lisp has that many languages don't,
consider the compile time type checking of dynamic type programs.
it's really nice to have a implementation that will catch:

(defun test()
(let ((x 1))
(setf x 'a)
(+ 1 a)))

as a type error at compile time, and its something I really miss when
I'm debugging other peoples code in languages like R or matlab.

It is loading more messages.
0 new messages