[racket] a little macro exercise

From: namekuseijin (namekuseijin at gmail.com)
Date: Sat Oct 9 14:35:27 EDT 2010

On Fri, Oct 8, 2010 at 10:04 PM, Shriram Krishnamurthi <sk at cs.brown.edu> wrote:
> One of my students recently sent me this needless email message:
>> Well, how would you do switch fall-through in Scheme? Could you
>> write a version of the case statement that does that?
> Since the honor of Racket was at stake (yes, we can have all the same
> stupid features the scripting languages have!)

and stupid it is!  from http://en.wikipedia.org/wiki/Switch_statement :

switch (n) {
  case 0:
    puts("You typed zero.");
  case 1:
  case 4:
  case 9:
    puts("n is a perfect square.");
  case 2:
    puts("n is an even number.");
  case 3:
  case 5:
  case 7:
    puts("n is a prime number.");
  case 6:
  case 8:
    puts("n is an even number.");
    puts("Only single-digit numbers are allowed.");

in the case of 2, it prints both "n is an even number." and "n is a
prime number." which is fine for 2, but may feel like a bug most
n00bs, since it looks like the last statement should apply only to the
"labels" 3,5 and 7.  Plus, for 4 it prints "n is a perfect square.",
but not "n is an even number.", which looks like it was overlooked by
the developer.  Fall-through is far less useful than one might think,
specially in light of a supposed canonical, instructive and short
example being flawed -- you can only imagine one in production use!

I'd rather try to persuade the n00b on why fall-through is bad (many
sources of subtle and hard-to-identify bugs, as even the wikipedia
article notices) and instead educate him on the fine art of How to
Design Programs... ;)

my take on plain ol' scheme for the above switch mess:

(define (properties n)
    (if (> n 9) "Only single-digit numbers are allowed."
        (fold '() (lambda (i o) (if (memq n (car i)) (cons (cdr i) o) o))
     `(((0) . "You typed zero.")
       ((1 4 9) . "n is a perfect square.")
       ((2 4 6 8) . "n is an even number.")
       ((2 3 5 7) . "n is a prime number.")))))

; given this non-tail-recursive fold
(define (fold init f ls) (if (null? ls) init (f (car ls) (fold init f
(cdr ls)))))

; testing
(map properties '(0 1 2 3 4 5 6 7 8 9 10))

Plus, it's shorter if you take fold for granted (and you should).

Posted on the users mailing list.