From: Ron Garret
Subject: Another cool Lispism
Date: 
Message-ID: <rNOSPAMon-5AA5B4.23385712102005@news.gha.chartermi.net>
Just collided head on with another limitation of Python:

Python dictionaries have a cool method called setdefault, which 
essentially does this:

d.setdefault(key, value) == if d.has_key(key):
                              return d[key]
                            else:
                              d[key] = value
                              return value

But suppose you were doing this:

d.setdefault(key, do_expensive_calculation())

You would want this to do:

if d.has_key(key):
  return d[key]
else:
  value = do_expensive_calculation()
  d[key] = value
  return value

but, of course, it doesn't do that, and there is no way to make it do 
that because Python has no macros.

In Lisp, of course, it is trivial to define a SETDEFAULT macro that does 
the Right Thing.

rg

From: Markus Weihs
Subject: Re: Another cool Lispism
Date: 
Message-ID: <pan.2005.10.13.10.01.12.895067@gmx.at>
Hm, I don't really understand what you mean. Could you show
a concrete example where Python fails?

Regards, Markus
From: Kaz Kylheku
Subject: Re: Another cool Lispism
Date: 
Message-ID: <1129224699.503960.286090@g14g2000cwa.googlegroups.com>
Markus Weihs wrote:
> Hm, I don't really understand what you mean. Could you show
> a concrete example where Python fails?

The issue is simply that the default value is computed whether or not
it is needed, in order to supply a call-by-value parameter for the
setdefault call.

The default value is only needed when the dictionary doesn't supply a
value for that key; otherwise, it is just discarded.

This is a common idiom when, for instance, dictionaries are used to
store configuration parameters, where you want default values for
properties that are not present.

The POSIX shell has a feature like that for evaluating variables, for
the same reason:

   ${FOO-bar}

will expand to the text "bar" if the variable FOO is not set, otherwise
it will expand to the value of that variable.

${FOO:-bar} will treat a blank variable as not being set.

The assignment for ${FOO:=bar} most closely resembles setdefault: it
stores the bar value to the FOO bariable if FOO is unset or blank.

Oh, and, guess what, the shell, or at least the GNU implementation of
it that I have here, is smart enough not to evaluate the default value
if the variable is non-blank.

   ${FOO:-$(ls -l)}

will not actually invoke the ls utility if FOO is set!  It has the
short-circuiting behavior.
From: Pascal Costanza
Subject: Re: Another cool Lispism
Date: 
Message-ID: <3r6q89FhoblgU1@individual.net>
Markus Weihs wrote:
> Hm, I don't really understand what you mean. Could you show
> a concrete example where Python fails?

Consider:

(or (gethash key table)
     (setf (gethash key table) (loop)))


Pascal

-- 
OOPSLA'05 tutorial on generic functions & the CLOS Metaobject Protocol
++++ see http://p-cos.net/oopsla05-tutorial.html for more details ++++
From: Revzala Haelmic
Subject: Re: Another cool Lispism
Date: 
Message-ID: <dildss$2a0$1@domitilla.aioe.org>
Pascal Costanza wrote:
> Consider:
> 
> (or (gethash key table)
>     (setf (gethash key table) (loop)))

Aha...

(defun set-default (table key value)
   (or (gethash key table)
     (setf (gethash key table) value)))

vs.

(defmacro set-default-macro (table key value)
   `(or (gethash ,key ,table)
     (setf (gethash ,key ,table) ,value)))

Looks like fun:

(defun yell (a b c)
   (format t "I was called!")
   (* (+ a b) c))

(defun test1 ()
   (let ((table (make-hash-table :test 'equal)))
     (loop repeat 10 do (set-default table "key" (yell 1 2 10)))))

(defun test2 ()
   (let ((table (make-hash-table :test 'equal)))
     (loop repeat 10 do (set-default-macro table "key" (yell 1 2 10)))))

It gives ~10x speedup only by setting ` and , correctly. Very cool.
I have to overcome my fear of ASDF and try to do something with libraries ;))
From: Pascal Costanza
Subject: Re: Another cool Lispism
Date: 
Message-ID: <3r6u7fFi6dj1U1@individual.net>
Revzala Haelmic wrote:
> Pascal Costanza wrote:
> 
>> Consider:
>>
>> (or (gethash key table)
>>     (setf (gethash key table) (loop)))
> 
> Aha...
> 
> (defun set-default (table key value)
>   (or (gethash key table)
>     (setf (gethash key table) value)))
> 
> vs.
> 
> (defmacro set-default-macro (table key value)
>   `(or (gethash ,key ,table)
>     (setf (gethash ,key ,table) ,value)))

Since you _can_ define set-default as a function, it's better to do so. 
Functions are available as first-class values (#'set-default), while 
macros are not (at least not in the "obviously" useful sense).

In other words, you can say (mapcar #'set-default ...), while you can't 
say (mapcar #'set-default-macro ...).

> It gives ~10x speedup only by setting ` and , correctly. Very cool.

If you are worried about performance, use a compiler-macro instead of a 
macro. It's very similar:

(define-compiler-macro set-default (table key value)
   `(or (gethash ,key ,table)
        (setf (gethash ,key ,table) ,value)))

The main difference is that you can still use #'set-default as a 
first-class value. Cool, eh? ;)

There is still a potential problem in the macro definition, though. 
Given the definition, key and table may be evaluated more than once at 
runtime. This can again lead to undesired behavior if the expressions 
for key and table contain side effects.

It's a good exercise to come up with an example in which the macro 
definition indeed fails, because it should become obvious then how to 
avoid it.

> I have to overcome my fear of ASDF and try to do something with 
> libraries ;))

Yep. ;)


Pascal

-- 
OOPSLA'05 tutorial on generic functions & the CLOS Metaobject Protocol
++++ see http://p-cos.net/oopsla05-tutorial.html for more details ++++
From: Revzala Haelmic
Subject: Re: Another cool Lispism
Date: 
Message-ID: <dim1rh$9sm$1@domitilla.aioe.org>
Pascal Costanza wrote:
> Since you _can_ define set-default as a function, it's better to do so. 
> Functions are available as first-class values (#'set-default), while 
> macros are not (at least not in the "obviously" useful sense).
> 
> In other words, you can say (mapcar #'set-default ...), while you can't 
> say (mapcar #'set-default-macro ...).

Very good advice, thanks.

> If you are worried about performance, use a compiler-macro instead of a 
> macro. It's very similar:
> 
> (define-compiler-macro set-default (table key value)
>   `(or (gethash ,key ,table)
>        (setf (gethash ,key ,table) ,value)))
> 
> The main difference is that you can still use #'set-default as a 
> first-class value. Cool, eh? ;)

Fantastic! :)) I suspect that define-compiler-macro has more possibilities than 
just declaring an inline function.

It was a bit tricky to understand why doesn't it work if I declare it with a 
name set-default-compiler-macro.

> There is still a potential problem in the macro definition, though. 
> Given the definition, key and table may be evaluated more than once at 
> runtime. This can again lead to undesired behavior if the expressions 
> for key and table contain side effects.
> 
> It's a good exercise to come up with an example in which the macro 
> definition indeed fails, because it should become obvious then how to 
> avoid it.

Yes, I ended up with this:

(defun set-default (table key value)
   (or (gethash key table)
       (setf (gethash key table) value)))

(define-compiler-macro set-default (table key value)
   (let ((table-name (gensym))
	(key-name (gensym)))
     `(let ((,table-name ,table)
	   (,key-name ,key))
       (progn
	(format t "Compiler macro works!~%")
	(or (gethash ,key-name ,table-name)
	    (setf (gethash ,key-name ,table-name) ,value))))))

And when this test...

(defun test4 ()
   (let ((table (make-hash-table :test 'equal)))
     (mapcar (lambda (x) (set-default table "key" (yell x 1 1)))
	    (list 1 2 3 4 5))))

...gave me this output...

	Compiler macro works!
	I was called!
	Compiler macro works!
	Compiler macro works!
	Compiler macro works!
	Compiler macro works!
	(2 2 2 2 2)

...I was very amazed :)))

>> I have to overcome my fear of ASDF and try to do something with 
>> libraries ;))
> 
> Yep. ;)

I even found out that ASDF is already in my Lispbox distribution. Now I'll give 
it a try with cl-ppcre.
From: Ron Garret
Subject: Re: Another cool Lispism
Date: 
Message-ID: <rNOSPAMon-1E245C.10544313102005@news.gha.chartermi.net>
In article <············@domitilla.aioe.org>,
 Revzala Haelmic <··@fck.org> wrote:

> Pascal Costanza wrote:
> > Since you _can_ define set-default as a function, it's better to do so. 
> > Functions are available as first-class values (#'set-default), while 
> > macros are not (at least not in the "obviously" useful sense).
> > 
> > In other words, you can say (mapcar #'set-default ...), while you can't 
> > say (mapcar #'set-default-macro ...).
> 
> Very good advice, thanks.

IMHO, this is being a little too clever, for two reasons.

First, if you're mapping set-default you're almost certainly doing 
something wrong.

Second, this compiler macro actually changes the semantics of 
set-default, and a compiler macro that changes the semantics of the code 
is a Bad Idea.  It will almost certainly come back to haunt you some day.

As an aside, Common Lisp is very much committed stylistically to having 
setters be macros rather than functions.  It is possible to have a 
functional approach to setters; T and Oaklisp (anyone remember Oaklisp?) 
both had a really cool (IMO) design where everything was an object with 
methods, and accessor functions had a method called SETTER which 
returned a function that did the obvious thing, e.g. (setter #'car) --> 
#'rplaca (except that T and Oaklisp were both Lisp-1's and so you didn't 
need the #', and rplaca was (sensibly IMO) spelled set-car!).  You did 
the equivalent of DEFSETF by changing the setter method.

CL explicitly rejected this approach in favor of the SETF macro.  There 
are legitimate arguments to be made for both sides.  The T/Oaklisp 
approach has a certain spare elegance to it that appeals to some people, 
but it is hard (though not impossible) to compile efficiently, and the 
ability to change setter methods on the fly invites a certain level of 
abuse.  The CL approach is easier to compile efficiently, and encourages 
people to design their code so that setf/setter methods don't change at 
run time, which is arguably the Right Thing.

IMHO, YMMV, etc. etc.

rg
From: Edi Weitz
Subject: Re: Another cool Lispism
Date: 
Message-ID: <uk6ghqkc5.fsf@agharta.de>
On Thu, 13 Oct 2005 10:54:43 -0700, Ron Garret <·········@flownet.com> wrote:

> As an aside, Common Lisp is very much committed stylistically to
> having setters be macros rather than functions.

But it lets you get away with functions as well:

  CL-USER 1 > (defun leftguy (x) (car x))
  LEFTGUY

  CL-USER 2 > (defun (setf leftguy) (v x) (setf (car x) v))
  (SETF LEFTGUY)

  CL-USER 3 > (defparameter *foo* (list 1 2))
  *FOO*

  CL-USER 4 > (setf (leftguy *foo*) 42)
  42

  CL-USER 5 > *foo*
  (42 2)

  CL-USER 6 > (setq *foo* (list (list 1 2 3) (list 4 5 6)))
  ((1 2 3) (4 5 6))

  CL-USER 7 > (mapcar #'(setf leftguy) (list 42 43) *foo*)
  (42 43)

  CL-USER 8 > *foo*
  ((42 2 3) (43 5 6))

  CL-USER 9 > #'(setf leftguy)
  #'(LAMBDA (V X) (DECLARE (LAMBDA-NAME (SETF LEFTGUY))) (BLOCK LEFTGUY (SETF (CAR X) V)))

  CL-USER 10 > (type-of *)
  TYPE::INTERPRETED-FUNCTION

  CL-USER 11 > (compile '(setf leftguy))
  (SETF LEFTGUY)
  NIL
  NIL

  CL-USER 12 > (type-of #'(setf leftguy))
  LOW:COMPILED-CODE

  CL-USER 13 > (typep #'(setf leftguy) 'function)
  T

  CL-USER 14 > (compiled-function-p #'(setf leftguy))
  T

> IMHO, YMMV, etc. etc.

OK... :)

Cheers,
Edi.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Ron Garret
Subject: Re: Another cool Lispism
Date: 
Message-ID: <rNOSPAMon-32D079.17225113102005@news.gha.chartermi.net>
In article <·············@agharta.de>, Edi Weitz <········@agharta.de> 
wrote:

> On Thu, 13 Oct 2005 10:54:43 -0700, Ron Garret <·········@flownet.com> wrote:
> 
> > As an aside, Common Lisp is very much committed stylistically to
> > having setters be macros rather than functions.
> 
> But it lets you get away with functions as well:
> 
>   CL-USER 1 > (defun leftguy (x) (car x))
>   LEFTGUY
> 
>   CL-USER 2 > (defun (setf leftguy) (v x) (setf (car x) v))
>   (SETF LEFTGUY)

I didn't say that CL forbade using functions as setters, only that it 
was "committed stylistically" to making them macros.  Having a setf 
macro expand to a function call is merely a special enough case that 
there is this (setf accessor) function name hack, but (setf accessor) as 
a function name is very different from (setter accessor) as a function 
call that returns a first-class function that serves as a setter.  For 
example, in T/Oaklisp you can do this:

(define (munge place accessor-list value-list)
  (map (lambda (accessor value) ((setter accessor) place value))
       accessor-list value-list)))

But there is no equivalent in CL.  (Not that this is necessarily a bad 
thing.  That's a pretty scary piece of code.)

rg
From: M Jared Finder
Subject: Re: Another cool Lispism
Date: 
Message-ID: <eKadnVCSUYRwTNLeRVn-qg@speakeasy.net>
Ron Garret wrote:
> 
> I didn't say that CL forbade using functions as setters, only that it 
> was "committed stylistically" to making them macros.  Having a setf 
> macro expand to a function call is merely a special enough case that 
> there is this (setf accessor) function name hack, but (setf accessor) as 
> a function name is very different from (setter accessor) as a function 
> call that returns a first-class function that serves as a setter.  For 
> example, in T/Oaklisp you can do this:
> 
> (define (munge place accessor-list value-list)
>   (map (lambda (accessor value) ((setter accessor) place value))
>        accessor-list value-list)))
> 
> But there is no equivalent in CL.  (Not that this is necessarily a bad 
> thing.  That's a pretty scary piece of code.)

That sounds like a Lisp-1 vs Lisp-2 distinction more than anything else. 
You can define such a setter function in CL, but it has to take a 
symbol, not a function.

(defun setter (symbol)
   (fdefinition (list 'setf symbol)))

CL-USER> (let ((c (cons 1 2)))
            (funcall (setter 'car) 10 c)
            c)
(10 . 2)

   -- MJF
From: Ron Garret
Subject: Re: Another cool Lispism
Date: 
Message-ID: <rNOSPAMon-0D78F1.10264714102005@news.gha.chartermi.net>
In article <······················@speakeasy.net>,
 M Jared Finder <·····@hpalace.com> wrote:

> Ron Garret wrote:
> > 
> > I didn't say that CL forbade using functions as setters, only that it 
> > was "committed stylistically" to making them macros.  Having a setf 
> > macro expand to a function call is merely a special enough case that 
> > there is this (setf accessor) function name hack, but (setf accessor) as 
> > a function name is very different from (setter accessor) as a function 
> > call that returns a first-class function that serves as a setter.  For 
> > example, in T/Oaklisp you can do this:
> > 
> > (define (munge place accessor-list value-list)
> >   (map (lambda (accessor value) ((setter accessor) place value))
> >        accessor-list value-list)))
> > 
> > But there is no equivalent in CL.  (Not that this is necessarily a bad 
> > thing.  That's a pretty scary piece of code.)
> 
> That sounds like a Lisp-1 vs Lisp-2 distinction more than anything else. 
> You can define such a setter function in CL, but it has to take a 
> symbol, not a function.
> 
> (defun setter (symbol)
>    (fdefinition (list 'setf symbol)))
> 
> CL-USER> (let ((c (cons 1 2)))
>             (funcall (setter 'car) 10 c)
>             c)
> (10 . 2)
> 
>    -- MJF

Ah!  I didn't know about fdefinition.  I stand corrected.

rg
From: Revzala Haelmic
Subject: Re: Another cool Lispism
Date: 
Message-ID: <dir3hq$add$1@domitilla.aioe.org>
M Jared Finder wrote:
> (defun setter (symbol)
>   (fdefinition (list 'setf symbol)))
> 
> CL-USER> (let ((c (cons 1 2)))
>            (funcall (setter 'car) 10 c)
>            c)
> (10 . 2)

In CLISP 2.34, it doesn't work:

FDEFINITION: undefined function #1=(SETF CAR)

Although (setter 'elt) works and results in
#<SYSTEM-FUNCTION SYSTEM::|(SETF ELT)|>

Is it right?
From: Ivan Boldyrev
Subject: Re: Another cool Lispism
Date: 
Message-ID: <7vib23-fo9.ln1@ibhome.cgitftp.uiggm.nsc.ru>
On 9263 day of my life Revzala Haelmic wrote:
>> (defun setter (symbol)
>>   (fdefinition (list 'setf symbol)))
> In CLISP 2.34, it doesn't work:
>
> FDEFINITION: undefined function #1=(SETF CAR)

AFAIR, it was fixed in CLISP 2.35.

-- 
Ivan Boldyrev

                  Sorry my terrible English, my native language is Lisp!
From: Alan Crowe
Subject: Re: Another cool Lispism
Date: 
Message-ID: <86r7aoo747.fsf@cawtech.freeserve.co.uk>
Ron Garret <·········@flownet.com> writes:
> For 
> example, in T/Oaklisp you can do this:
> 
> (define (munge place accessor-list value-list)
>   (map (lambda (accessor value) ((setter accessor) place value))
>        accessor-list value-list)))
> 
> But there is no equivalent in CL.  (Not that this is necessarily a bad 
> thing.  That's a pretty scary piece of code.)

CL comes quite close

(defvar list (list 1 2 3 4 5 6 7 8 9 10))

(munge list 
       '(first third fifth)
       '(one three five))

list => (ONE 2 THREE 4 FIVE 6 7 8 9 10)

where I have defined munge

  (defun munge (place accessor-list value-list)
    (mapcar
     (lambda(accessor value)
       (funcall (fdefinition (list 'setf accessor))
                value
                place))
     accessor-list
     value-list))

Not that this is necessarily a good thing :-)

Alan Crowe
Edinburgh
Scotland
From: Lars Brinkhoff
Subject: Re: Another cool Lispism
Date: 
Message-ID: <85y84weafq.fsf@junk.nocrew.org>
Alan Crowe <····@cawtech.freeserve.co.uk> writes:
>   (defun munge (place accessor-list value-list)
>     (mapcar
>      (lambda(accessor value)
>        (funcall (fdefinition (list 'setf accessor))
>                 value
>                 place))
>      accessor-list
>      value-list))

I don't think accessors are guaranteed to define setf functions, i.e.
(fboundp '(setf first)) may return nil.
From: Bulent Murtezaoglu
Subject: Re: Another cool Lispism
Date: 
Message-ID: <87u0fk8mx4.fsf@p4.internal>
>>>>> "LB" == Lars Brinkhoff <·········@nocrew.org> writes:
[...]
    LB> I don't think accessors are guaranteed to define setf
    LB> functions, i.e.  (fboundp '(setf first)) may return nil.

Yes 

"Defining a setf expander F does not cause the setf function (setf F) 
to become defined." [1]

and

"For each standardized accessor function F, unless it is explicitly
documented otherwise, it is implementation-dependent whether the
ability to use an F form as a setf place is implemented by a setf
expander or a setf function. Also, it follows from this that it is
implementation-dependent whether the name (setf F) is fbound." [2]

cheers,

BM

[1] http://www.lisp.org/HyperSpec/Body/fun_fboundp.html

[2] http://www.lisp.org/HyperSpec/Body/sec_5-1-1-2.html
From: Lars Brinkhoff
Subject: Re: Another cool Lispism
Date: 
Message-ID: <85slv4e4l4.fsf@junk.nocrew.org>
Bulent Murtezaoglu writes:
> Lars Brinkhoff writes:
> > I don't think accessors are guaranteed to define setf functions,
> > i.e.  (fboundp '(setf first)) may return nil.
>
> "Defining a setf expander F does not cause the setf function (setf
> F) to become defined." and "For each standardized accessor function
> F, unless it is explicitly documented otherwise, it is
> implementation-dependent whether the ability to use an F form as a
> setf place is implemented by a setf expander or a setf function."

So, here's another version to consider:

  (defun setter (accessor)
    (if (fboundp `(setf ,accessor))
        (fdefinition `(setf ,accessor))
        (lambda (new-value object)
          (setf (,accessor object) new-value))))

Note that this only allows one argument to the setf expander.  The
full solution is left as an exercise to the reader.
From: ·@b.c
Subject: Re: Another cool Lispism
Date: 
Message-ID: <fr5vk158ug9blumdjsjk16h8h37urpl33v@4ax.com>
On Fri, 14 Oct 2005 13:11:03 +0200, Lars Brinkhoff
<·········@nocrew.org> wrote:

>  (defun setter (accessor)
>    (if (fboundp `(setf ,accessor))
>        (fdefinition `(setf ,accessor))
>        (lambda (new-value object)
>          (setf (,accessor object) new-value))))

That code has clearly not been tested.
From: Revzala Haelmic
Subject: Re: Another cool Lispism
Date: 
Message-ID: <dimbu3$sdi$2@domitilla.aioe.org>
Ron Garret wrote:
>  Revzala Haelmic <··@fck.org> wrote:
>>Pascal Costanza wrote:
>>>In other words, you can say (mapcar #'set-default ...), while you can't 
>>>say (mapcar #'set-default-macro ...).
>>
>>Very good advice, thanks.
> 
> IMHO, this is being a little too clever, for two reasons.
> 
> First, if you're mapping set-default you're almost certainly doing 
> something wrong.

Well, I'm not so sure because I didn't see any example pro, and neither contra. 
My experience is not so broad to make such decisions in advance.

That was just an example to show another one "cool Lispism" with 
define-compiler-macro. I don't know will I ever use set-default and this 
approach for it. Usually I tend to do the most simple thing while it works. ;)

> Second, this compiler macro actually changes the semantics of 
> set-default, and a compiler macro that changes the semantics of the code 
> is a Bad Idea.  It will almost certainly come back to haunt you some day.

I don't understand what semantics exactly does it change? Or, more precisely, 
whether it does it worse than other macros? If you are talking about the order 
and probability of it's arguments' evaluation, is it really that dangerous?

> The CL approach is easier to compile efficiently, and encourages 
> people to design their code so that setf/setter methods don't change at 
> run time, which is arguably the Right Thing.

Eek... changing setter functions at runtime? I haven't reached the level where 
such problems arise :) Can you make an example, please?
From: Ron Garret
Subject: Re: Another cool Lispism
Date: 
Message-ID: <rNOSPAMon-ACD590.17315613102005@news.gha.chartermi.net>
In article <············@domitilla.aioe.org>,
 Revzala Haelmic <··@fck.org> wrote:

> Ron Garret wrote:
> >  Revzala Haelmic <··@fck.org> wrote:
> >>Pascal Costanza wrote:
> >>>In other words, you can say (mapcar #'set-default ...), while you can't 
> >>>say (mapcar #'set-default-macro ...).
> >>
> >>Very good advice, thanks.
> > 
> > IMHO, this is being a little too clever, for two reasons.
> > 
> > First, if you're mapping set-default you're almost certainly doing 
> > something wrong.
> 
> Well, I'm not so sure because I didn't see any example pro, and neither 
> contra. 
> My experience is not so broad to make such decisions in advance.

Then you should just take my word for it :-)

> > Second, this compiler macro actually changes the semantics of 
> > set-default, and a compiler macro that changes the semantics of the code 
> > is a Bad Idea.  It will almost certainly come back to haunt you some day.
> 
> I don't understand what semantics exactly does it change?

It changes when the default form is evaluated.

> Or, more precisely, 
> whether it does it worse than other macros? If you are talking about the 
> order 
> and probability of it's arguments' evaluation, is it really that dangerous?

Yes.  Consider the following:

(defvar cache (make-dictionary))
(defun get-info (key)
  (set-default cache key (get-info-from-very-slow-server key)))

(This is in fact the exact situation that made me realize that there was 
a problem with Python's setdefault.)

> > The CL approach is easier to compile efficiently, and encourages 
> > people to design their code so that setf/setter methods don't change at 
> > run time, which is arguably the Right Thing.
> 
> Eek... changing setter functions at runtime? I haven't reached the level 
> where 
> such problems arise :) Can you make an example, please?

No.  If you have to ask you're not ready to know.

rg
From: Revzala Haelmic
Subject: Re: Another cool Lispism
Date: 
Message-ID: <dimclq$tqg$1@domitilla.aioe.org>
Ron Garret wrote:
>> Second, this compiler macro actually changes the semantics of 
>> set-default, and a compiler macro that changes the semantics of the 
>> code is a Bad Idea.  It will almost certainly come back to haunt you 
>> some day.
> 
> I don't understand what semantics exactly does it change? Or, more 
> precisely, whether it does it worse than other macros? If you are 
> talking about the order and probability of it's arguments' evaluation, 
> is it really that dangerous?

Ahh, I understood it (from the recent message of Pascal Constanza) -- compiler 
macro is required to be a twin of a function or a macro. Since it behaves 
differently, it can cause troubles in situations where one can't figure out, 
whether the function or the compiler macro will be called.

Thanks.
From: Edi Weitz
Subject: Re: Another cool Lispism
Date: 
Message-ID: <ur7apqrp9.fsf@agharta.de>
On Thu, 13 Oct 2005 20:26:23 +0400, Revzala Haelmic <··@fck.org> wrote:

> Fantastic! :)) I suspect that define-compiler-macro has more
> possibilities than just declaring an inline function.

  <http://wiki.alu.org/lisp-user-meeting-amsterdam-april-2004> (see Arthur's talk)
  <http://cooking-with-lisp.blogspot.com/2004/07/trying-to-grok-compiler-macros.html>

> I even found out that ASDF is already in my Lispbox
> distribution. Now I'll give it a try with cl-ppcre.

The file api.lisp in CL-PPCRE includes some uses of compiler macros.
Basically, if the compiler can figure out that a regular expression is
constant it'll compile it into a closure at compile time already.

Cheers,
Edi.


-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Revzala Haelmic
Subject: Re: Another cool Lispism
Date: 
Message-ID: <dimbtq$sdi$1@domitilla.aioe.org>
Edi Weitz wrote:
>   <http://wiki.alu.org/lisp-user-meeting-amsterdam-april-2004> (see Arthur's talk)
>   <http://cooking-with-lisp.blogspot.com/2004/07/trying-to-grok-compiler-macros.html>

Yes, that is what I read when trying to figure out why 
set-default-compiler-macro doesn't work.

> The file api.lisp in CL-PPCRE includes some uses of compiler macros.
> Basically, if the compiler can figure out that a regular expression is
> constant it'll compile it into a closure at compile time already.

I don't understand why there is (load-time-value ...), but not (eval-when 
(:compile-toplevel) ...)

Thanks for examples and pointers. The idea of explicitly made compile-time 
optimizations is very interesting. I didn't see it in any other language.
From: Edi Weitz
Subject: Re: Another cool Lispism
Date: 
Message-ID: <ufyr5qjws.fsf@agharta.de>
On Thu, 13 Oct 2005 23:18:15 +0400, Revzala Haelmic <··@fck.org> wrote:

> Edi Weitz wrote:
>
>> The file api.lisp in CL-PPCRE includes some uses of compiler
>> macros.  Basically, if the compiler can figure out that a regular
>> expression is constant it'll compile it into a closure at compile
>> time already.
>
> I don't understand why there is (load-time-value ...), but not
> (eval-when (:compile-toplevel) ...)

First of all, I wasn't exact in my description above.  The whole story
is: The compiler arranges at compile time (with the help of the
compiler macro) that the scanner (i.e. the closure) will be created
only once - at load time.  This is what LOAD-TIME-VALUE is for.

If LOAD-TIME-VALUE weren't there than a form like, say,

  (SCAN ".*" TARGET)

would be compiled to something like

  (SCAN #<foo> TARGET)

where #<foo> is a literal function object.  This is fine while you're
in the REPL but COMPILE-FILE wouldn't generally know how to dump such
an object to a FASL file - see the recent discussions about this
topic:

  <http://groups.google.com/group/comp.lang.lisp/browse_frm/thread/93dc7062fffdbd68/e81156ab6274ef31#e81156ab6274ef31>

Why do you think EVAL-WHEN is needed?

> Thanks for examples and pointers. The idea of explicitly made
> compile-time optimizations is very interesting. I didn't see it in
> any other language.

Of course... :)

Cheers,
Edi.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Pascal Costanza
Subject: Re: Another cool Lispism
Date: 
Message-ID: <3r7n2tFi4n6dU1@individual.net>
Revzala Haelmic wrote:
> Pascal Costanza wrote:
> 
>> If you are worried about performance, use a compiler-macro instead of 
>> a macro. It's very similar:
>>
>> (define-compiler-macro set-default (table key value)
>>   `(or (gethash ,key ,table)
>>        (setf (gethash ,key ,table) ,value)))
>>
>> The main difference is that you can still use #'set-default as a 
>> first-class value. Cool, eh? ;)
> 
> Fantastic! :)) I suspect that define-compiler-macro has more 
> possibilities than just declaring an inline function.

Not really. A Common Lisp implementation is allowed to ignore compiler 
macros for any reason, just like it is allowed to ignore inline 
declarations. So you don't have a guarantee that they are used. This 
especially means that they should be semantically equivalent to the 
function definition. (For reasonable definitions of "semantically 
equivalent".)

> Yes, I ended up with this:
> 
> (defun set-default (table key value)
>   (or (gethash key table)
>       (setf (gethash key table) value)))
> 
> (define-compiler-macro set-default (table key value)
>   (let ((table-name (gensym))
>     (key-name (gensym)))
>     `(let ((,table-name ,table)
>        (,key-name ,key))
>       (progn
>     (format t "Compiler macro works!~%")
>     (or (gethash ,key-name ,table-name)
>         (setf (gethash ,key-name ,table-name) ,value))))))

OK, this looks good. However, I made a mistake in my previous posting, 
and it carried through to your version. (Ron's reply made me realize 
this.) There is an important difference between the two versions, so the 
compiler macro is indeed _not_ semantically equivalent.

Sorry for this glitch.


Pascal

-- 
OOPSLA'05 tutorial on generic functions & the CLOS Metaobject Protocol
++++ see http://p-cos.net/oopsla05-tutorial.html for more details ++++
From: Pascal Costanza
Subject: Re: Another cool Lispism
Date: 
Message-ID: <3r7napFi4n6dU2@individual.net>
Pascal Costanza wrote:
> Revzala Haelmic wrote:
> 
>> I suspect that define-compiler-macro has more 
>> possibilities than just declaring an inline function.
> 
> Not really. A Common Lisp implementation is allowed to ignore compiler 
> macros for any reason, just like it is allowed to ignore inline 
> declarations. So you don't have a guarantee that they are used. This 
> especially means that they should be semantically equivalent to the 
> function definition. (For reasonable definitions of "semantically 
> equivalent".)

And I forgot to continue here: The advantage of using a compiler macro 
over an inline declaration is that a programmer can use specific 
knowledge about a function that a compiler cannot infer. For simple 
inlining, it's sufficient to use an inline declaration (in Common Lisp 
implementations that support this).


Pascal

-- 
OOPSLA'05 tutorial on generic functions & the CLOS Metaobject Protocol
++++ see http://p-cos.net/oopsla05-tutorial.html for more details ++++
From: Alain Picard
Subject: Re: Another cool Lispism
Date: 
Message-ID: <87br1socco.fsf@memetrics.com>
Revzala Haelmic <··@fck.org> writes:

> Yes, I ended up with this:
>
> (defun set-default (table key value)
>    (or (gethash key table)
>        (setf (gethash key table) value)))
>

This is off topic (of compiler macros), but you
may with to think about the fact that NIL is a valid
value to have in a table.

 
From: Revzala Haelmic
Subject: Re: Another cool Lispism
Date: 
Message-ID: <dionfl$9pl$1@domitilla.aioe.org>
Alain Picard wrote:
> Revzala Haelmic <··@fck.org> writes:
>>(defun set-default (table key value)
>>   (or (gethash key table)
>>       (setf (gethash key table) value)))
> 
> This is off topic (of compiler macros), but you
> may with to think about the fact that NIL is a valid
> value to have in a table.

That's right.

(defmacro set-default-macro (table key value)
   (let ((table-name (gensym))
	(key-name (gensym))
	(content (gensym))
	(exists (gensym)))
     `(let ((,table-name ,table)
	   (,key-name ,key))
       (multiple-value-bind
	    (,content ,exists) (gethash ,key-name ,table-name)
	(if ,exists
	    ,content
	    (setf (gethash ,key-name ,table-name) ,value))))))

And the next thing I'll write is macro that folds macro body with gensyms.
From: Revzala Haelmic
Subject: Re: Another cool Lispism
Date: 
Message-ID: <dioop3$bov$1@domitilla.aioe.org>
Hey -- my code wasn't indented like that. Maybe it's worth switching to Gnus?
I'll try again:

(defmacro set-default-macro (table key value)
   (let ((table-name (gensym))
	(key-name (gensym))
	(content (gensym))
	(exists (gensym)))
     `(let ((,table-name ,table)
	   (,key-name ,key))
       (multiple-value-bind (,content ,exists) (gethash ,key-name ,table-name)
	(if ,exists
	    ,content
	    (setf (gethash ,key-name ,table-name) ,value))))))
From: Lars Brinkhoff
Subject: Re: Another cool Lispism
Date: 
Message-ID: <858xwuet8h.fsf@junk.nocrew.org>
Revzala Haelmic <··@fck.org> writes:
> And the next thing I'll write is macro that folds macro body with gensyms.

That's called with-gensyms.
From: Revzala Haelmic
Subject: Re: Another cool Lispism
Date: 
Message-ID: <dilcnt$vjn$1@domitilla.aioe.org>
Markus Weihs wrote:
> Hm, I don't really understand what you mean. Could you show
> a concrete example where Python fails?
> 
> Regards, Markus
> 

I think his point can be shown on this example:

def loud():
     print "Ouch! I was called!"
     return 1

d = {}
for i in xrange(10):
     d.setdefault("key", loud())

When called, it prints "Ouch! I was called!" 10 times. Ron wants "Ouch! I was 
called!" to be printed only once. In Python, I see only one way to achieve this:

def setdefault_with_call(dict, key, func, *args, **kwargs):
     if not dict.has_key(key):
         dict[key] = func(*args, **kwargs)
     return dict[key]

We can use it like this:

def loud2(a, b, c = 10):
     print "Ouch! I was called too!"
     return (a + b) * c

d = {}
for i in xrange(10):
     setdefault_with_call(d, "key", loud2, 1, 2, c = 10)

And we get only one "Ouch! I was called too!" message.
I suppose in Python this trick is used very frequently.
From: Markus Weihs
Subject: Re: Another cool Lispism
Date: 
Message-ID: <pan.2005.10.13.10.38.15.11177@gmx.at>
> I think his point can be shown on this example:

This is a very strange example :-)
So you want a dict where the keys have a function as their value?

    d = {}

    for i in range(10):
        d.setdefault(i, loud)
 
    d[3]()
    # prints: Ouch! I was called!


Regards, Markus
From: Revzala Haelmic
Subject: Re: Another cool Lispism
Date: 
Message-ID: <dilf3v$4p5$1@domitilla.aioe.org>
Markus Weihs wrote:
> This is a very strange example :-)
> So you want a dict where the keys have a function as their value?

No.

The first part of the example is not strange at all. I suspect you are talking 
about the second part. If you inspect this dictionary in Python environment, 
you'll see it has numbers inside, not functions.

What I meant is calling an expensive function to set a default value of an 
element of a dictionary. The setdefault_with_call is just a fancy way to do that.

>     d = {}
> 
>     for i in range(10):
>         d.setdefault(i, loud)
>  
>     d[3]()
>     # prints: Ouch! I was called!

Yes, what about a dictionary that calls a function to return a value by a given 
key? So just call d[3] prints "Ouch! I was called!". Hmm... from the 
mathematical point of view, this dictionary is a function itself. Hmm... each 
dictionary is a function. So, it's not worth the trouble, only if you don't want 
to make it look like dictionary, i.e. with square braces. If so, it is possible 
in Python too -- see section 3.3.5 of Python Reference Manual. In Lisp, they 
even don't care about such things. And I like that.
From: Markus Weihs
Subject: Re: Another cool Lispism
Date: 
Message-ID: <pan.2005.10.13.11.45.23.525821@gmx.at>
> The first part of the example is not strange at all. I suspect you are 
> talking  about the second part. If you inspect this dictionary in 
> Python environment,  you'll see it has numbers inside, not functions.
When I inspect your dictionary, I get 
    {'key': 1}
That's not strange? What would be the use of this? (And why do you
need a loop to set one key?)


> What I meant is calling an expensive function to set a default value of an 
> element of a dictionary. The setdefault_with_call is just a fancy way to do 
> that.
Ok, this version gives me this dictionary:
    {'key': 30}

Why can't you do it like this:
    def loud(a,b,c=10):
        return (a+b)*c
    d = {}
    for i in range(10):
        d.setdefault(i, loud(1,2,c=10))

I guess I still totally miss the point of the problem :) Sorry if
that's the case. I'm a bit silly today :)
From: R. Mattes
Subject: Re: Another cool Lispism
Date: 
Message-ID: <pan.2005.10.13.12.01.24.121759@mh-freiburg.de>
On Thu, 13 Oct 2005 13:45:24 +0200, Markus Weihs wrote:

>
> I guess I still totally miss the point of the problem :) Sorry if
> that's the case. I'm a bit silly today :)

it took me a while to get the point as well - the problem
description isn't very clear and the examples didn't help
that much ...

I think the OP's point is: the expression given as the _default_ value
in the call of  d.setdefault(key, value) is evaluated _before_ the
test for the presence of 'key' in the dictionary. So, in a call:

 d.setdefault("my key", doExpensiveCalculation(42))

doExpensiveCalculation is called even in cases where d contains
"my key". Basically a question of evaluation order ...
So it burns down to "Python doesn't have macros and hence lacks
control over evaluation order". Big deal.

 Cheers Ralf Mattes
From: Markus Weihs
Subject: Re: Another cool Lispism
Date: 
Message-ID: <pan.2005.10.13.13.15.35.544652@gmx.at>
Ah, ok, now I understand it ;) Thank you.
From: Barry Margolin
Subject: Re: Another cool Lispism
Date: 
Message-ID: <barmar-D853FA.00421414102005@comcast.dca.giganews.com>
In article <······························@mh-freiburg.de>,
 "R. Mattes" <··@mh-freiburg.de> wrote:

> I think the OP's point is: the expression given as the _default_ value
> in the call of  d.setdefault(key, value) is evaluated _before_ the
> test for the presence of 'key' in the dictionary. So, in a call:
> 
>  d.setdefault("my key", doExpensiveCalculation(42))
> 
> doExpensiveCalculation is called even in cases where d contains
> "my key". Basically a question of evaluation order ...
> So it burns down to "Python doesn't have macros and hence lacks
> control over evaluation order". Big deal.

Does it have closures, or anything that can be made to act similar?  
Instead of passing a default value to setdefault, it could take a 
closure that would be called to get the default value when necessary.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Revzala Haelmic
Subject: Re: Another cool Lispism
Date: 
Message-ID: <dionqj$aae$1@domitilla.aioe.org>
Barry Margolin wrote:
> Does it have closures, or anything that can be made to act similar?  
> Instead of passing a default value to setdefault, it could take a 
> closure that would be called to get the default value when necessary.

Maybe this is not so widely known, but it does. You may want to have a look at 
http://docs.python.org/ref/callable-types.html for callable objects, 
http://docs.python.org/ref/sequence-types.html for dictionary-like objects,
http://docs.python.org/ref/yield.html and 
http://docs.python.org/tut/node11.html#SECTION0011900000000000000000 for simple 
generators and iterators.
From: R. Mattes
Subject: Re: Another cool Lispism
Date: 
Message-ID: <pan.2005.10.14.19.05.11.269684@mh-freiburg.de>
On Fri, 14 Oct 2005 20:53:37 +0400, Revzala Haelmic wrote:

> Barry Margolin wrote:
>> Does it have closures, or anything that can be made to act similar?  
>> Instead of passing a default value to setdefault, it could take a 
>> closure that would be called to get the default value when necessary.
> 
> Maybe this is not so widely known, but it does. You may want to have a look at 
> http://docs.python.org/ref/callable-types.html for callable objects, 
> http://docs.python.org/ref/sequence-types.html for dictionary-like objects,
> http://docs.python.org/ref/yield.html and 
> http://docs.python.org/tut/node11.html#SECTION0011900000000000000000 for simple 
> generators and iterators.

I must have lost my babelfish ... how's that relevant to either closures
or the OP's remarks? 
Neither callable instances (which are _not_ closures btw. since they don't
close over anything) nor closures will  solve the problem:

class myCallable:
  def __call__(self):
    return a_costly_computation()

callable = myCallable()

d.setdefault("key", callable)

will set the default value to the instance 'callable' and 
a lookup of "key" will return just that instance (and not the
value of a_costly_computation). 

d.setdefault("key", callable())

will invoke a_costly_computation at invokation, not during 
dictionary lookup.
This is defined semantic of setdefault. BTW, Lisp's hash lookup
default is similar:

 ; SLIME 2005-09-29
CL-USER> (defvar my-hash (make-hash-table))
MY-HASH
CL-USER> (gethash 'key my-hash 'my-default)
MY-DEFAULT
NIL
CL-USER> (defun a-costly-computation ()
	   (sleep 10) 42)
A-COSTLY-COMPUTATION
CL-USER> (gethash 'key my-hash (a-costly-computation))
;; 10 seconds pause here
42
NIL
CL-USER> 

The power of lisp might make it easy to provide syntactic 
enough sugar to create a "lazzy" version, that's all.

 Cheers RalfD
 
From: Barry Margolin
Subject: Re: Another cool Lispism
Date: 
Message-ID: <barmar-69D3A9.20485714102005@comcast.dca.giganews.com>
In article <······························@mh-freiburg.de>,
 "R. Mattes" <··@mh-freiburg.de> wrote:

> On Fri, 14 Oct 2005 20:53:37 +0400, Revzala Haelmic wrote:
> 
> > Barry Margolin wrote:
> >> Does it have closures, or anything that can be made to act similar?  
> >> Instead of passing a default value to setdefault, it could take a 
> >> closure that would be called to get the default value when necessary.
> > 
> > Maybe this is not so widely known, but it does. You may want to have a look 
> > at 
> > http://docs.python.org/ref/callable-types.html for callable objects, 
> > http://docs.python.org/ref/sequence-types.html for dictionary-like objects,
> > http://docs.python.org/ref/yield.html and 
> > http://docs.python.org/tut/node11.html#SECTION0011900000000000000000 for 
> > simple 
> > generators and iterators.
> 
> I must have lost my babelfish ... how's that relevant to either closures
> or the OP's remarks? 
> Neither callable instances (which are _not_ closures btw. since they don't
> close over anything) nor closures will  solve the problem:
> 
> class myCallable:
>   def __call__(self):
>     return a_costly_computation()
> 
> callable = myCallable()
> 
> d.setdefault("key", callable)
> 
> will set the default value to the instance 'callable' and 
> a lookup of "key" will return just that instance (and not the
> value of a_costly_computation). 

I was hypothesizing a variant of setdefault that would *call* the 
callable instance when necessary, not just store it in the table.

-- 
Barry Margolin, ······@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
From: Revzala Haelmic
Subject: Re: Another cool Lispism
Date: 
Message-ID: <diqkun$ii1$1@domitilla.aioe.org>
Barry Margolin wrote:
> I was hypothesizing a variant of setdefault that would *call* the 
> callable instance when necessary, not just store it in the table.

I gave an example of such setdefault earlier in this thread. Was it that bad or 
incomprehensible? ;)
From: Revzala Haelmic
Subject: Re: Another cool Lispism
Date: 
Message-ID: <diql64$itg$1@domitilla.aioe.org>
R. Mattes wrote:
> I must have lost my babelfish ... how's that relevant to either closures
> or the OP's remarks? 

OK, a couple of links more:
Babelfish is here: http://babelfish.altavista.com :))

> Neither callable instances (which are _not_ closures btw. since they don't
> close over anything) nor closures will  solve the problem:

What about closures, their most important (and characteristic) property is 
ability to keep and share the state. Probably, this is what is called "closing 
over". The "classes" in some other languages were just a reinvention of 
closures. Methods of class instance often share the common state, aren't they?

Saying "oh, it's callable object, not a function" looks a bit funny -- as if you 
discovered something new :))

Also, I'd like to provide a link to Paul Prescod's article "On the Relationship 
Between Python and Lisp": http://www.prescod.net/python/IsPythonLisp.html
He gives an excellent example of what we are talking about in the section 'The 
"Accumulator" Micro-benchmark'.

I agree with you that closures will not solve problem. At least, I do not see 
how they can help.

> class myCallable:
>   def __call__(self):
>     return a_costly_computation()
> 
> callable = myCallable()
> 
> d.setdefault("key", callable)
> 
> will set the default value to the instance 'callable' and 
> a lookup of "key" will return just that instance (and not the
> value of a_costly_computation). 

Which is equivalent to d.setdefault("key", a_costly_computation), isn't it?

> BTW, Lisp's hash lookup default is similar:
 >
> [skipped]
 >
> The power of lisp might make it easy to provide syntactic 
> enough sugar to create a "lazzy" version, that's all.

Yes, the "syntactic" is the right word. For the language without syntax :)))