[plt-scheme] a few questions (was: Scheme questions?)

From: Danny Yoo (dyoo at hkn.eecs.berkeley.edu)
Date: Sun Dec 4 18:50:04 EST 2005


> Okay, as a newcomer to the language, letrec seems reasonably intuitive
> to me, named let is mysterious. (I think of letrec as binding a set of
> symbols to lambda expressions, where the expressions may involve mutual
> recursion. It seems to me that in
>
> (let whatever ((..)...)
>
> whatever is used in the body of the expression as if though it were
> itself a lambda expression, but how this works (and what the correct
> syntax is) seems rather mysterious.


Hi Gregory,

I tend to use named-let as a way of writing loops in Scheme.  For example,
if we were to try to find an element in a list, we might write something
like this:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (my-member elt lst)
  (let loop ((lst lst))
    (cond ((null? lst) (list))
          ((equal? elt (car lst)) lst)
          (else (loop (cdr lst))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


And although this can be rewritten by using just letrec:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (my-member2 elt lst)
  (letrec
      ((loop
        (lambda (lst)
          (cond ((null? lst) (list))
                ((equal? elt (car lst)) lst)
                (else (loop (cdr lst)))))))
    (loop lst)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

it feels slightly more verbose than the named-let version.  So as far as
power is concerned, named-let doesn't give us anything new, but the
named-let syntactic sugar makes it a little more convenience to define
these one-shot looping functions on the fly.


The example above shows that we can look at named-lets as really a letrec
in disguise.  We can say this more formally, to tell Scheme how to do this
syntax translation from a named-let to a real letrec, by defining a
syntax:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-syntax my-named-let
  (syntax-rules ()
    ((_ function-name ((id val) ...)
        e1
        e2 ...)

     (letrec
         ((function-name
           (lambda (id ...)
             e1
             e2 ...)))
       (function-name val ...)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


This syntax definition tries to show how we might take something that uses
a named-let, and describe it in terms of a letrec.  And we can use it!

;;;;;;
> (my-named-let pairing-loop ((names '(danny andy jerry)))
    (if (<= (length names) 1)
        (list)
        (cons (list (car names) (cadr names))
              (pairing-loop (cdr names)))))
((danny andy) (andy jerry))
;;;;;;



If we're curious, we can watch this expansion in action by explicitely
calling PLT Scheme's syntax expander to take our expression above and
expand it once:

;;;;;;
> (syntax-object->datum
   (expand-once
    '(my-named-let pairing-loop ((names '(danny andy jerry)))
        (if (<= (length names) 1)
            (list)
            (cons (list (car names) (cadr names))
                  (pairing-loop (cdr names)))))))
(letrec ((pairing-loop (lambda (names) (if (<= (length names) 1) (list)
(cons (list (car names) (cadr names)) (pairing-loop (cdr names)))))))
(pairing-loop (quote (danny andy jerry))))
;;;;;;



Hmmm, the result does look like it works, but it's a slight mess to read.
Let's pretty-up that output to make it better indented, using the
pretty-printing library in PLT Scheme's 'mzlib' standard library:

(http://download.plt-scheme.org/doc/299.400/html/mzlib/mzlib-Z-H-34.html)

;;;;;;
> (require (lib "pretty.ss"))
>
> (pretty-display
   (syntax-object->datum
    (expand-once
     '(my-named-let loop ((names '(danny andy jerry)))
        (if (<= (length names) 1)
            (list)
            (cons (list (car names) (cadr names))
                  (loop (cdr names))))))))
(letrec ((loop
          (lambda (names)
            (if (<= (length names) 1)
              (list)
              (cons (list (car names) (cadr names)) (loop (cdr names)))))))
  (loop '(danny andy jerry)))
;;;;;;


I hope this helps!




Posted on the users mailing list.