[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

IF macro


You recently posted about your IF macro:
	(if (foo? x) 
            (then (bar x) (baz y))
	    (else (quux z)))
and asked why T didn't have this as a standard. I don't speak for the
Scheme community in general, but I'll tell you where I think this macro
clashes with the Scheme language standard: keywords.

The syntax of a given special form is generally given by one keyword:
the one appearing in the car of the form. All the rest of the syntax
is reflected in the list structure of the form. This is a general
rule for Scheme special forms. In your macro, the THEN and ELSE 
keywords are just noise words -- you could remove them, and the
information needed to separate the consequent (THEN) clauses from
the alternate (ELSE) clauses would still be captured by the extra
layer of parens. Now, you could say that the IF form will take
extra noise words THEN and ELSE in its grammar for extra readability,
but note that IF would now be inconsistent with the low-noise
parsimonious style of the entire rest of the language. This is probably
not the way to go for a core form like IF.

IF, as defined in Scheme, is a very simple beast. It takes exactly
three subforms, and has a very simple rule for evaluating them. It
is the primitive conditional form. *Your* IF, on the other
hand, has a more complicated (sophisticated) syntax and semantics.
This might make it more useful, but is inappropriate for *the*
primitive conditional form. Primitive forms should be orthogonal.
If you want more sophisticated structures, you compose them from
your simple primitives (in this case, IF and BLOCK).

This approach to primitive forms has helped keep Scheme small and
powerful, as opposed to bloated, baroque languages like PL/I, and

Now, I think internal keywords are a fine thing, for hairy, non-primitive
syntax. I use a loop package that has internal keywords, and I
think it is a model of perspicuity:
	(loop (incr i from 0)
	      (for x in list1)
	      (for y in list2)	
	      (save (list i x y)))
Here, INCR, FOR, and SAVE are all keywords special to the loop macro.
But, LOOP is *not* a primitive form, part of the T definition. It's
a language extension to T, implemented as a macro package.

The problem with IF, as you've defined (and named) it, is that it is
incompatible with the standard IF form. Your implementation cleverly avoids
this by checking for the presence of the internal keywords, and implementing
the standard IF semantics if they are not present. Bear in mind, however, that
this is not correct, just heuristic. If the poor user happens to have a
function named THEN, and he calls it inside an IF, and your macro gets ahold
of it, he loses:
    (if (foo? 3) (then 3)) ; Call function THEN on 3.

There are two ways to fix this clash that I can think of:
    - Change the name of your macro. If T were case-sensitive like franz,
      you could do what Foderaro did, and call yours "If", not "if". 
      But it isn't. You could call it "iph", which is gross. Better
      names fail me.
    - Leave poor IF alone, but define THEN and ELSE to be macros synonymous
      with BLOCK. John Ellis does this with his lisps. Now your IF is
      implemented in a manner consistent with the standard IF. 

      Actually, ELSE is a standard T identifier, so maybe you ought to
      use THEN and ELS. Or something.