< a quantity that can be divided into another a whole number of time />

Named let in Scheme

August 28, 2022

In one of my last posts, I discussed Common Lisp loop macro. I just finished reading The Adventures of a Pythonista in Schemeland, by Michele Simionato. Although it is now more than 10 years old, it is a very good read that I recommend to people interested in Scheme, especially if they want to learn more about macros. In this series of articles, the authors discusses some idiomatic construct in Scheme. Beside recursion, he shows an example of a for-loop form which I got interested in. Chicken Scheme, which is the current implementation of Scheme dialect I use,1 has a for-each construct which allows to write stuff like this:

(define xs '(0 1 2 3 4))
(for-each print xs)

This is more limited than what’s available in Racket’s iterations and comprehensions forms but it has the merit of being there. Note that there are more involved constructs for iterating over sequences.

In Scheme, a named let is the standard way to build such a for-like expression. Let’s see an example:

(import srfi-48)  ;; see also Chicken extras module

(let loop ((i 0))
  (unless (= i 5)
     (format #t "~a " i)
     (loop (+ i 1))))

This is well explained on Stack Overflow, especially how it relates to tail-recursion. See also Python to Scheme to Assembly, Part 1: Recursion and Named Let, for another use case of recursion and accumulators. Now, checking the SICP book, Abelson & coll. discuss an iterative Fibonacci procedure using a named let:

(define (fib n)
  (let fib-iter ((a 1)
                 (b 0)
                 (count n))
    (if (= coutn 0)
        (fib-iter (+ a b) a (- count 1)))))

A named let can also be an alternative to the following procedure (Exercise 4.8):

(define (let->combination expr)
   (cons (make-lambda (let-vars expr) (let-body expr))
         (let-inits expr)))

Here is one solution from Scheme wiki:

(define (let->combination expr)
     (if (named-let? expr)
           (list (named-let->func expr)
                 (cons (named-let-func-name expr) (named-let-func-inits expr))))
         (cons (make-lambda (let-vars expr)
               (list (let-body expr)))
               (let-inits expr))))

See also Eli Bendersky’s solutions in Common Lisp.

Instead of a for loop, let’s consider a while loop. It is not much different, since in this case we have a predicate that helps stopping the iteration and a default case. See also this discussion on call/cc on SO. There’s a bunch of useful utilities in the “standard prelude” of Programming Praxis. It targets R5RS, and some of its forms are already available in Chicken Scheme or Racket, but it is worth taking a look at the code. For instance, it features a macro for a while loop construct:

(define-syntax while
  (syntax-rules ()
    ((while pred? body ...)
      (do () ((not pred?)) body ...))))

Example of use:

(let ((loop 4))
  (while (< 0 loop)
         (display loop)
         (set! loop (- loop 1))))
=> 4321

Incidentally, I just found that Jacques Chazarain also discussed the case of Fibonacci numbers and named let constructs, and he even proposed a different solution to the while loop macro, using pretty old syntax and non-hygienic macro:2

(define-macro (while test . body)
  `(letrec ((loop (lambda ()
                    (if ,test
                      (begin ,@body (loop))))))

There is nevertheless a slight issue with the formulation above: the local function loop may happen to mask a variable bound in the let statement. To remedy this problem, one needs to use gensym to generate the name of the local loop construct, as illustrated below:

(define-macro (while test . body)
  (let ((loop (gensym)))
    `(letrec ((,loop (lambda ()
                    (if ,test
                      (begin ,@body (,loop))))))

A related topic is also discussed on Chicken Scheme website.

Now that I’m catching up on my RSS feeds, I can suggest a further reading, especially for Lisp hackers: Named Lambda and Named Let, by Joe Marshall.

♪ MIKA • Blue Eyes

  1. It is the first Scheme implementation I heard about, around 2007, via Jan de Leeuw’s mailing list on statistical computing. I wrote toy example, stopped using it and forgot about it until I got back to Scheme four or five years ago. ↩︎

  2. Chazarain, J. Programmer avec Scheme. International Thomson Publishing, 1996. ↩︎

See Also

» Simply Scheme » Split-apply-combine in Scheme » Scheming with Vim » Welch t-test in Scheme » Syracuse numbers