Abandoned

Since infogami has been abandoned by its creators, I’m out too. Back to web.fisher.cx for me. Everything that was here is there.

Robert Fisher

Just thinking out loud

Scheme is inside out

If you register and log in you can add comments to my pages. If viewing the main blog page, click the # underneath an entry to comment on it.

One of the things that tends to frustrate me a bit while writing Scheme code is that it tends to be inside out.

Suppose I want to write a procedure to convert a string to all upper case. One way to do it would be with these steps:

  1. Convert the string to a list of characters
  2. Convert each character to upper case
  3. Convert the list back into a string

(Not necessarily the best way, but a way that allows me to illustrate my point.)

But look at it in Scheme:

(define (string-upcase string)
  (list->string (map char-upcase (string->list string))))

It's backwards! When things are more complex, however, you start to see that it is really inside out.

Now, sometimes I write inside out code like this is C too. Likewise, sometimes Scheme code is sequential. But C tends to be more sequential than Scheme, & Scheme tends to be more inside out than C.

So, could I make the Scheme sequential?

(define (string-upcase x)
  (set! x (string->list x))
  (set! x (map char-upcase x))
  (set! x (list->string x))
  x)

But that's ugly & not very Scheme-ish.

(define (string-upcase string)
  (let* ((original-list (string->list string))
         (upcase-list (map char-upcase original-list))
         (upcase-string (list->string upcase-list)))
    upcase-string))

That's more Scheme-ish, perhaps, but even uglier.


(There's also the case where you end up lifting a common subexpression into a let & you have to partially restructure inside out code as sequential. I'd really like things to be more uniform to make these kinds of code transformations easier. Failing that, though, some code to automate lifting common subexpressions could be very cool.)

(Could you have an "it"? Something like Perl's $_?)


(foo-macro (string-upcase string)
  (string->list string)
  (map char-upcase it)
  (list->string it))

Hmm. Turning this into...

(list->string (map char-upcase (string->list string))))

...with syntax-rules doesn't look very easy. Probably a job for syntax-case. It's be cool if you could also make it do automatic lifting into a let if it were used twice in the same expression. But that seems a little too ambitious. Better to warn users that using it multiple times in one expression can lead to massive code bloat. Let them insert lets by hand.

I think changing it to a let* form might not be too hard with syntax-rules.


OK, here's what I've got so far:

(define-syntax seq
  (syntax-rules ()
    ((_ it form)
     form)
    ((_ it form . forms)
     (let ((it form))
       (seq it . forms)))))

This will turn...

(seq it
     (string->list s)
     (map char-upcase it)
     (list->string it)))

...into...

(let ((it (string->list s)))
  (let ((it (map char-upcase)))
    (list->string it)))

On a related note, here is a (untested) macro by psykotic on reddit to allow introducing local variable bindings without needing additional indention:

(define-syntax block
  (syntax-rules (var)
    ((block (var x e) body ...)
     (let ((x e)) (block body ...)))
    ((block e)
     e)
    ((block e body ...)
     (begin e (block body ...)))))

Scheme