From: Bruce Butterfield
Subject: CL idiom for Scheme's named let
Date: 
Message-ID: <1132773611.411387.71800@g14g2000cwa.googlegroups.com>
I am porting some Scheme code to do XPath processing (Oleg Kiselyov's
SXPath package) and I would like to know how best to convert code that
looks something like this to CL:

;;;;;;;;;;;;;;;;;;;;
(let outer ((nodeset nodeset) (path path))
 ...
    (let reducer ((nodeset
                        (if (symbol? (caar path))
                            ((select-kids (node-typeof? (caar path)))
nodeset)
                            (outer nodeset (caar path))))
                       (reducing-path (cdar path)))
        (cond
           ((null? reducing-path) (outer nodeset (cdr path)))
           ((number? (car reducing-path))
            (reducer ((node-pos (car reducing-path)) nodeset)
                         (cdr reducing-path)))
...
;;;;;;;;;;;;;;;;;;;;;;;

My equivalent looks like:
;;;;;;;;;;;;;;;;;;;;;;;;;;
(labels ((outer (nodeset path)
         ...)
  (let ((reducing-path (cdar path))
         (nodeset (if (symbolp (caar path))
                          (funcall (node-typeofp (caar path)) nodeset)
                          (outer nodeset (caar path))))
    (labels ((reducer (nodes rpath)
      (cond
         ((null rpath) (outer nodeset (cdr path)))
         ((numberp (car rpath))
          (reducer (funcall (node-pos (car rpath)) nodeset)
                       (cdr rpath)))
...
     (reducer nodeset reducing-path)
...
  (outer nodeset path)
;;;;;;;;;;;;;;;;;;;;;;

Would it be better to remove transform this into an iterative form or
is there a more natural way to do the recursion in CL?

From: Wade Humeniuk
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <p05hf.126566$y_1.21503@edtnps89>
Bruce Butterfield wrote:
> I am porting some Scheme code to do XPath processing (Oleg Kiselyov's
> SXPath package) and I would like to know how best to convert code that
> looks something like this to CL:
> 
> ;;;;;;;;;;;;;;;;;;;;
> (let outer ((nodeset nodeset) (path path))
>  ...
>     (let reducer ((nodeset
>                         (if (symbol? (caar path))
>                             ((select-kids (node-typeof? (caar path)))
> nodeset)
>                             (outer nodeset (caar path))))
>                        (reducing-path (cdar path)))
>         (cond
>            ((null? reducing-path) (outer nodeset (cdr path)))
>            ((number? (car reducing-path))
>             (reducer ((node-pos (car reducing-path)) nodeset)
>                          (cdr reducing-path)))
> ...
> ;;;;;;;;;;;;;;;;;;;;;;;

One way ....  You have to a little careful with this code
about properly returning values (you have to return values
from PROG expicitly with something like RETURN).  It
would probably be best to wrap the NAMED-LETs in a BLOCK
and safely do a RETURN-FROM when done.


(defmacro named-let (name vars &body body)
   (let ((var-names (mapcar #'car vars)))
     `(macrolet ((,name (&rest new-values)
                   `(progn
                      (setf ,@(loop for var in ',var-names
                                    for new-value in new-values
                                    collect var
                                    collect new-value))
                      (go ,',name))))
        (prog ,vars
          ,name
          ,@body))))

CL-USER 9 > (named-let string-check
                 ((string "test"))
               (cond
                ((equal string "done") (return string))
                ((equal string "test")
                 (string-check "next"))
                ((equal string "next")
                 (string-check "done"))))
"done"

CL-USER 10 >

Wade
From: Kalle Olavi Niemitalo
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <87ek57c8ka.fsf@Astalo.kon.iki.fi>
Wade Humeniuk <··················@telus.net> writes:

> One way ....  You have to a little careful with this code
> about properly returning values (you have to return values
> from PROG expicitly with something like RETURN).  It
> would probably be best to wrap the NAMED-LETs in a BLOCK
> and safely do a RETURN-FROM when done.

>        (prog ,vars
>          ,name
>          ,@body))))

Well you could use (return (progn ,@body)).
In principle, a non-NIL BLOCK would be safer than PROG, but I
guess that will only matter when one is writing new code in CL,
rather than just translating from Scheme.

I think one could also use a local function here, rather than a
local macro.  This would allow the function to be passed as an
argument to other functions (e.g. APPLY).  It still wouldn't
work right if saved in a variable and called after the loop has
already been left; but I expect such uses will be even rarer
than call-with-current-continuation, and easier to work around.
From: Wade Humeniuk
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <488hf.134226$yS6.81191@clgrps12>
Kalle Olavi Niemitalo wrote:
> Wade Humeniuk <··················@telus.net> writes:
> 
> 
>>One way ....  You have to a little careful with this code
>>about properly returning values (you have to return values
>>from PROG expicitly with something like RETURN).  It
>>would probably be best to wrap the NAMED-LETs in a BLOCK
>>and safely do a RETURN-FROM when done.
> 
> 
>>       (prog ,vars
>>         ,name
>>         ,@body))))
> 
> 
> Well you could use (return (progn ,@body)).
> In principle, a non-NIL BLOCK would be safer than PROG, but I
> guess that will only matter when one is writing new code in CL,
> rather than just translating from Scheme.
> 
> I think one could also use a local function here, rather than a
> local macro.  This would allow the function to be passed as an
> argument to other functions (e.g. APPLY).  It still wouldn't
> work right if saved in a variable and called after the loop has
> already been left; but I expect such uses will be even rarer
> than call-with-current-continuation, and easier to work around.

Yes,

Modified version,

(defmacro named-let (name vars &body body)
   (let* ((var-names (mapcar #'car vars))
          (block-name (gensym "NAMED-LET-"))
          (name-function-args (mapcar (lambda (var)
                                        (declare (ignore var))
                                        (gensym)) var-names)))
     `(block ,block-name
        (prog ,vars
          ,name
          (flet ((,name ,name-function-args
                   ,@(mapcar (lambda (var value)
                               `(setf ,var ,value))
                             var-names
                             name-function-args)
                   (go ,name)))
            (return-from ,block-name (progn ,@body)))))))

CL-USER 3 > (named-let string-check
                 ((string "test"))
               (cond
                ((equal string "done") string)
                ((equal string "test")
                 (string-check "next"))
                ((equal string "next")
                 (string-check "done"))))
"next"

Macroexpanded

(BLOCK #:NAMED-LET-551
   (PROG ((STRING "test"))
    STRING-CHECK (FLET ((STRING-CHECK (#:G552)
                          (SETF STRING #:G552)
                          (GO STRING-CHECK)))
                   (RETURN-FROM #:NAMED-LET-551
                     (PROGN
                       (COND ((EQUAL STRING "done") STRING)
                             ((EQUAL STRING "test")
                              (STRING-CHECK "next"))
                             ((EQUAL STRING "next")
                              (STRING-CHECK "done"))))))))
From: Bruce Butterfield
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <1132795498.245717.190610@g43g2000cwa.googlegroups.com>
Very cool, thanks, I'll give that a shot.

The more general question I had, however, I'll restate: is there a more
'CL-like' way to do this or is the answer to mimic named let?
From: Rob Warnock
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <v-CdnYD0FPZL3BjeRVn-uA@speakeasy.net>
Bruce Butterfield <·····@open-tek.com> wrote:
+---------------
| The more general question I had, however, I'll restate: is there a
| more 'CL-like' way to do this or is the answer to mimic named let?
+---------------

No, using LABELS as a transliteration of named-LET is perfectly
fine, provided that you don't depend on the tail-call optimization
property that Scheme promises with named-LET.

Actually, LABELS is really closer to Scheme's LETREC, in that
it allows mutual recursion between more than one function (which
named-LET doesn't!). But LABELS isn't a perfect direct translation
of LETREC, since the latter can be used to bind *both* function
and variable values, whereas in CL those need separate forms.
So you might have to do a bit of data flow analysis to tease
apart a horrendously-complex LETREC into an equivalent set of
nested LET & LABELS forms.


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Wade Humeniuk
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <rKahf.129118$y_1.63760@edtnps89>
Bruce Butterfield wrote:
> Very cool, thanks, I'll give that a shot.
> 
> The more general question I had, however, I'll restate: is there a more
> 'CL-like' way to do this or is the answer to mimic named let?
> 

In this case I would mimic named let.  Right off
I would not want to fool with the algorithm as written.  Its not obvious
what it does (for various reasons, one of them being that it is Scheme).
Modifying this and other occurences is fraught with problems.  So for
me its best to leave well enough alone and just concentrate on getting
it working with the least amount of effort.  If the Scheme version was
modified and fixed it would be easier to merge those changes into
your forked version.

If this was done in CL I would have hoped that some higher level constructs
were used (maybe CLOS for the nodesets and reducing-paths).

So the moral of the story for me is to use the Chameleon attributes of
CL to port the app.

Wade
From: Marijn
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <1132823285.959700.191680@g47g2000cwa.googlegroups.com>
Wade Humeniuk schreef:
> (defmacro named-let (name vars &body body)
>    (let* ((var-names (mapcar #'car vars))
>           (block-name (gensym "NAMED-LET-"))
>           (name-function-args (mapcar (lambda (var)
>                                         (declare (ignore var))
>                                         (gensym)) var-names)))
>      `(block ,block-name
>         (prog ,vars
>           ,name
>           (flet ((,name ,name-function-args
>                    ,@(mapcar (lambda (var value)
>                                `(setf ,var ,value))
>                              var-names
>                              name-function-args)
>                    (go ,name)))
>             (return-from ,block-name (progn ,@body)))))))

This does not appear to work when the named let is used for a
non-iterative recursion. Maybe this was your intention (to make it do
an iteration even on implementations that don't do tail-call
optimization), but I've always found named lets the most useful for
weird complex recursive things. I use this macro a lot:

(defmacro nlet (name (&rest arguments) &body body)
  `(labels ((,name ,(mapcar #'car arguments) ,@body))
    (,name ,@(mapcar #'cadr arguments))))

By the way, are there any major CL implementations that do not optimize
tail calls? Coming from Scheme, I kind of tend to assume tail calls are
okay.

Marijn Haverbeke
From: Pascal Costanza
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <3ulf7dF11eutfU1@individual.net>
Marijn wrote:

> By the way, are there any major CL implementations that do not optimize
> tail calls?

I don't think so. In some cases you must switch it on, and it is 
implementation-dependent how to do that.

When translating Scheme code to Common Lisp, I would only worry about 
this issue if it indeed turns out to be a problem.


Pascal

-- 
My website: http://p-cos.net
Closer to MOP & ContextL:
http://common-lisp.net/project/closer/
From: John Thingstad
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <op.s0q70vfhpqzri1@mjolner.upc.no>
On Thu, 24 Nov 2005 10:08:05 +0100, Marijn <········@hotmail.com> wrote:

> Wade Humeniuk schreef:
>> (defmacro named-let (name vars &body body)
>>    (let* ((var-names (mapcar #'car vars))
>>           (block-name (gensym "NAMED-LET-"))
>>           (name-function-args (mapcar (lambda (var)
>>                                         (declare (ignore var))
>>                                         (gensym)) var-names)))
>>      `(block ,block-name
>>         (prog ,vars
>>           ,name
>>           (flet ((,name ,name-function-args
>>                    ,@(mapcar (lambda (var value)
>>                                `(setf ,var ,value))
>>                              var-names
>>                              name-function-args)
>>                    (go ,name)))
>>             (return-from ,block-name (progn ,@body)))))))
>
> This does not appear to work when the named let is used for a
> non-iterative recursion. Maybe this was your intention (to make it do
> an iteration even on implementations that don't do tail-call
> optimization), but I've always found named lets the most useful for
> weird complex recursive things. I use this macro a lot:
>
> (defmacro nlet (name (&rest arguments) &body body)
>   `(labels ((,name ,(mapcar #'car arguments) ,@body))
>     (,name ,@(mapcar #'cadr arguments))))
>
> By the way, are there any major CL implementations that do not optimize
> tail calls? Coming from Scheme, I kind of tend to assume tail calls are
> okay.
>
> Marijn Haverbeke
>

All CL's I have used (Corman , Allegro, LispWorks, CMUCL) have  
tail-recursion.
It is implementation dependent what turns it on.
< (declare (optimize (speed <number 0-3>) (debug <same>)...)) >
Typically a high debug setting turns tail call elimination off
while a high speed setting turns it on.
Typically speed > debug --> tail call elimination
The default settings of optimation options vary.
So on some it is turned on by default in some implementations but not in  
others.
This makes this kinda thing a piccle to port.

-- 
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
From: Russell McManus
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <87fypkv7l8.fsf@cl-user.org>
"John Thingstad" <··············@chello.no> writes:

> On Thu, 24 Nov 2005 10:08:05 +0100, Marijn <········@hotmail.com> wrote:
>
>> Wade Humeniuk schreef:
>>> (defmacro named-let (name vars &body body)
>>>    (let* ((var-names (mapcar #'car vars))
>>>           (block-name (gensym "NAMED-LET-"))
>>>           (name-function-args (mapcar (lambda (var)
>>>                                         (declare (ignore var))
>>>                                         (gensym)) var-names)))
>>>      `(block ,block-name
>>>         (prog ,vars
>>>           ,name
>>>           (flet ((,name ,name-function-args
>>>                    ,@(mapcar (lambda (var value)
>>>                                `(setf ,var ,value))
>>>                              var-names
>>>                              name-function-args)
>>>                    (go ,name)))
>>>             (return-from ,block-name (progn ,@body)))))))
>>
>> This does not appear to work when the named let is used for a
>> non-iterative recursion. Maybe this was your intention (to make it do
>> an iteration even on implementations that don't do tail-call
>> optimization), but I've always found named lets the most useful for
>> weird complex recursive things. I use this macro a lot:
>>
>> (defmacro nlet (name (&rest arguments) &body body)
>>   `(labels ((,name ,(mapcar #'car arguments) ,@body))
>>     (,name ,@(mapcar #'cadr arguments))))
>>
>> By the way, are there any major CL implementations that do not optimize
>> tail calls? Coming from Scheme, I kind of tend to assume tail calls are
>> okay.

Here's a macro that I just tried to write that really turns a named
let into a "goto with arguments", i.e. no recursion.  It's been a
while since I worked with scheme; does this match the semantics of
Scheme's named let?

(defmacro nlet (var bindings &body body)
  (let* ((top (gensym "TOP"))
	 (retval (gensym "RETVAL"))
	 (vars (mapcar #'car bindings))
	 (tmps (mapcar (lambda (var) (gensym (symbol-name var))) vars)))
    `(let ,bindings
      (let (,retval)
	(tagbody ,top
	   (macrolet ((,var (,@tmps)
			`(progn
			  (setf ,,@(loop for var in vars
					 for tmp in tmps
					 collect ``,',var
					 collect ``,,tmp))
			  (go ,',top))))
	     (setf ,retval (progn ,@body))))
	,retval))))

(nlet my-list-length ((l '(1 2 3))
		      (n 0))
  (if (null l)
      n
      (my-list-length (cdr l) (1+ n))))

=> 3

-russ
From: Marcin 'Qrczak' Kowalczyk
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <87sltka2bs.fsf@qrnik.zagroda>
Russell McManus <···············@yahoo.com> writes:

> Here's a macro that I just tried to write that really turns a named
> let into a "goto with arguments", i.e. no recursion.  It's been a
> while since I worked with scheme; does this match the semantics of
> Scheme's named let?

Not quite:

((list-ref
  (let again ((i 0))
    (if (> i 5)
        '()
        (cons (lambda () i) (again (+ i 1)))))
  3)) ==> 3

(funcall (nth 3
  (nlet again ((i 0))
    (if (> i 5)
        '()
        (cons #'(lambda () i) (again (1+ i)))))))
  --> FUNCALL: undefined function NIL

Another case:

((list-ref
  (let again ((i 5) (acc '()))
    (if (negative? i)
        acc
        (again (- i 1) (cons (lambda () i) acc))))
  3)) ==> 3

(funcall (nth 3
  (nlet again ((i 5) (acc '()))
    (if (< i 0)
        acc
        (again (1- i) (cons #'(lambda () i) acc))))))
  ==> 0

-- 
   __("<         Marcin Kowalczyk
   \__/       ······@knm.org.pl
    ^^     http://qrnik.knm.org.pl/~qrczak/
From: Russell McManus
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <87br07v6nh.fsf@cl-user.org>
Marcin 'Qrczak' Kowalczyk <······@knm.org.pl> writes:

> Russell McManus <···············@yahoo.com> writes:
>
>> Here's a macro that I just tried to write that really turns a named
>> let into a "goto with arguments", i.e. no recursion.  It's been a
>> while since I worked with scheme; does this match the semantics of
>> Scheme's named let?
>
> Not quite:
>
> ((list-ref
>   (let again ((i 0))
>     (if (> i 5)
>         '()
>         (cons (lambda () i) (again (+ i 1)))))
>   3)) ==> 3
>
> (funcall (nth 3
>   (nlet again ((i 0))
>     (if (> i 5)
>         '()
>         (cons #'(lambda () i) (again (1+ i)))))))
>   --> FUNCALL: undefined function NIL
>
> Another case:
>
> ((list-ref
>   (let again ((i 5) (acc '()))
>     (if (negative? i)
>         acc
>         (again (- i 1) (cons (lambda () i) acc))))
>   3)) ==> 3
>
> (funcall (nth 3
>   (nlet again ((i 5) (acc '()))
>     (if (< i 0)
>         acc
>         (again (1- i) (cons #'(lambda () i) acc))))))

The first case is a call in a non-tail position.  I'd have to write a
code walker and introduce a local function to handle that one, similar
to the earlier posted solutions.

I'm wondering whether it's possible to write in portable CL named-let
such that it guarantees iterative behavior when tail calls are made,
supports funcall, and works right for non-tail calls.  Maybe I'll hack
on this a bit more and see how far I can take it...

-russ
From: Marcin 'Qrczak' Kowalczyk
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <87mzjre7yq.fsf@qrnik.zagroda>
Russell McManus <···············@yahoo.com> writes:

> I'm wondering whether it's possible to write in portable CL named-let
> such that it guarantees iterative behavior when tail calls are made,
> supports funcall, and works right for non-tail calls.

It depends which kinds of tail calls are supposed to be guaranteed to
be run without growing the stack.


Example 1. An indirect call (it doesn't help that it ultimately
becomes a self call):

(define (identity x) x)

(define (countdown n)
  ; Silly example
  (let again ((i n))
    (if (positive? i)
        ((identity again) (- i 1)))))


Example 2. A direct non-self tail call:

(define (products l)
  ; The sum of products of all pairs of elements of l
  (let next-outer ((outer-list l) (outer-acc 0))
    (if (null? outer-list)
        outer-acc
        (let ((x (car outer-list)))
          (let next-inner ((inner-list l) (inner-acc outer-acc))
            (if (null? inner-list)
                (next-outer (cdr outer-list) inner-acc)
                (next-inner (cdr inner-list)
                            (+ inner-acc (* x (car inner-list))))))))))


Implementing full TCO in a language which doesn't provide it itself
requires whole-program rewriting. Without rewriting, the question is
which subset of cases we want to be TCO'd.

I suppose it is archievable to support full tail recursion (direct
self tail calls), and that any sensible larger subset requires
whole-program rewriting.

Are there Common Lisp implementations which don't perform TCO on tail
recursion by default? Or at least with suitable options (perhaps
debugging turned off)?

-- 
   __("<         Marcin Kowalczyk
   \__/       ······@knm.org.pl
    ^^     http://qrnik.knm.org.pl/~qrczak/
From: Russell McManus
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <87r793gsgq.fsf@cl-user.org>
Marcin 'Qrczak' Kowalczyk <······@knm.org.pl> writes:

> Russell McManus <···············@yahoo.com> writes:
>
>> I'm wondering whether it's possible to write in portable CL
>> named-let such that it guarantees iterative behavior when tail
>> calls are made, supports funcall, and works right for non-tail
>> calls.
>
> It depends which kinds of tail calls are supposed to be guaranteed
> to be run without growing the stack.

The macro that I am thinking of writing would look for combinations
with operators referring to the named let symbol (direct calls in your
parlance), which are also in a tail position, and use a GO in these
cases.  There would also be a local function introduced that would be
picked up for all other cases.

I think the above approach would work for example 2 but not example 1.

-russ
From: Wade Humeniuk
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <8sEhf.209042$ir4.147936@edtnps90>
Marijn wrote:

> 
> This does not appear to work when the named let is used for a
> non-iterative recursion. Maybe this was your intention (to make it do
> an iteration even on implementations that don't do tail-call
> optimization), but I've always found named lets the most useful for
> weird complex recursive things. I use this macro a lot:
> 

It is/was my undestanding that a invoking the name in a named let
resulted in a non-conditional jump (with no expectation that the
jump will return a value).  Thus ordinary recursion could not be done.  So
for the following simple factorial example is invalid:


(let fact ((n 10))
    (if (<= n 2) 1 (* n (fact (1- n))))

and would have to coded something like,

(let fact ((n 10) (result 1))
    (if (<= n 2) result
       (progn (multf result n) (fact (1- n) result))))

Wade




> (defmacro nlet (name (&rest arguments) &body body)
>   `(labels ((,name ,(mapcar #'car arguments) ,@body))
>     (,name ,@(mapcar #'cadr arguments))))
> 
> By the way, are there any major CL implementations that do not optimize
> tail calls? Coming from Scheme, I kind of tend to assume tail calls are
> okay.
> 
> Marijn Haverbeke
> 
From: Rob Warnock
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <PpmdneVKmpSH2xXenZ2dnUVZ_tednZ2d@speakeasy.net>
Wade Humeniuk  <··················@telus.net> wrote:
+---------------
| It is/was my undestanding that a invoking the name in a named let
| resulted in a non-conditional jump (with no expectation that the
| jump will return a value). Thus ordinary recursion could not be done.
+---------------

Sorry, that's incorrect. The name in a named LET is bound to an
ordinary Scheme procedure, just as if a LETREC [or LABELS, in CL]
had been used [so that the bound name is visible within the
procedure body]. See R5RS Section 4.2.4 "Iteration":

    "Named let" ... has the same syntax and semantics as ordinary let
    except that <variable> is bound within <body> to a procedure whose
    formal arguments are the bound variables and whose body is <body>.
    Thus the execution of <body> may be repeated by invoking the procedure
    named by <variable>.

That is:

    (let foo ((arg1 init1)
	      ...
	      (argN initN))
      ...body...)

is *identical* to [and in some Scheme implementations may simply
macroexpand to]:

    (letrec ((foo (lambda (arg1 ... argN)
	       ...body...)))
      (foo init1 ...initN))

Or in CL:

    (labels ((foo (arg1 ... argN)
	       ...body...))
      (foo init1 ...initN))

+---------------
| So for the following simple factorial example is invalid:
| (let fact ((n 10))
|     (if (<= n 2) 1 (* n (fact (1- n))))
+---------------

No, that works just fine, once you fix the bug in it!  ;-}  ;-}

    > (let fact ((n 10))
        (if (<= n 1) 1 (* n (fact (1- n)))))
    3628800
    > 

Similarly, multiple self-calls within the body also work:

    > (let fib ((n 10))
	(if (<= n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))
    89
    > 


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: Marcin 'Qrczak' Kowalczyk
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <87k6evprb2.fsf@qrnik.zagroda>
····@rpw3.org (Rob Warnock) writes:

>     (let foo ((arg1 init1)
> 	      ...
> 	      (argN initN))
>       ...body...)
>
> is *identical* to [and in some Scheme implementations may simply
> macroexpand to]:
>
>     (letrec ((foo (lambda (arg1 ... argN)
> 	       ...body...)))
>       (foo init1 ...initN))

According to R5RS the name should be bound only in body, so it should be:

      ((letrec ((foo (lambda (arg1 ... argN)
  	       ...body...)))
        foo) init1 ...initN)

and e.g. Guile gets this wrong.

-- 
   __("<         Marcin Kowalczyk
   \__/       ······@knm.org.pl
    ^^     http://qrnik.knm.org.pl/~qrczak/
From: Wade Humeniuk
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <jB_hf.214169$ir4.104531@edtnps90>
Rob Warnock wrote:
> Wade Humeniuk  <··················@telus.net> wrote:
> +---------------
> | It is/was my undestanding that a invoking the name in a named let
> | resulted in a non-conditional jump (with no expectation that the
> | jump will return a value). Thus ordinary recursion could not be done.
> +---------------
> 
> Sorry, that's incorrect. The name in a named LET is bound to an
> ordinary Scheme procedure, just as if a LETREC [or LABELS, in CL]
> had been used [so that the bound name is visible within the
> procedure body]. See R5RS Section 4.2.4 "Iteration":
> 
>     "Named let" ... has the same syntax and semantics as ordinary let
>     except that <variable> is bound within <body> to a procedure whose
>     formal arguments are the bound variables and whose body is <body>.
>     Thus the execution of <body> may be repeated by invoking the procedure
>     named by <variable>.
> 
> That is:
> 
>     (let foo ((arg1 init1)
> 	      ...
> 	      (argN initN))
>       ...body...)
> 
> is *identical* to [and in some Scheme implementations may simply
> macroexpand to]:
> 
>     (letrec ((foo (lambda (arg1 ... argN)
> 	       ...body...)))
>       (foo init1 ...initN))
> 
> Or in CL:
> 
>     (labels ((foo (arg1 ... argN)
> 	       ...body...))
>       (foo init1 ...initN))
> 

So much for Scheme's "minimal" set of procedures.

Wade
From: Marcin 'Qrczak' Kowalczyk
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <87irufe7r7.fsf@qrnik.zagroda>
Wade Humeniuk <····················@telus.net> writes:

>> is *identical* to [and in some Scheme implementations may simply
>> macroexpand to]:
[...]

> So much for Scheme's "minimal" set of procedures.

Huh? Scheme never claimed to provide only procedures which can't be
implemented in terms of others. On the contrary:

"1.3.1. Primitive, library, and optional features

[...]

To aid in understanding and implementing Scheme, some features are
marked as /library/. These can be easily implemented in terms of the
other, primitive, features. They are redundant in the strict sense of
the word, but they capture common patterns of usage, and are therefore
provided as convenient abbreviations."

-- 
   __("<         Marcin Kowalczyk
   \__/       ······@knm.org.pl
    ^^     http://qrnik.knm.org.pl/~qrczak/
From: Marcin 'Qrczak' Kowalczyk
Subject: Re: CL idiom for Scheme's named let
Date: 
Message-ID: <87veyf1ki6.fsf@qrnik.zagroda>
Wade Humeniuk <····················@telus.net> writes:

>> is *identical* to [and in some Scheme implementations may simply
>> macroexpand to]:
[...]

> So much for Scheme's "minimal" set of procedures.

Huh? Scheme never claimed to provide only procedures which can't be
implemented in terms of others. On the contrary:

"1.3.1. Primitive, library, and optional features

[...]

To aid in understanding and implementing Scheme, some features are
marked as /library/. These can be easily implemented in terms of the
other, primitive, features. They are redundant in the strict sense of
the word, but they capture common patterns of usage, and are therefore
provided as convenient abbreviations."

'Let' (both named and plain) is indeed marked as "library syntax".

-- 
   __("<         Marcin Kowalczyk
   \__/       ······@knm.org.pl
    ^^     http://qrnik.knm.org.pl/~qrczak/