[plt-scheme] macros, special forms, and lazy functions

From: Eli Barzilay (eli at barzilay.org)
Date: Thu Dec 10 16:03:35 EST 2009

On Dec 10, Stephen Bloch wrote:
> On Dec 10, 2009, at 2:09 PM, Jos Koot replied to my questions:
> >> As far as my beginning students are concerned, "and" and "or" are   
> >> functions with contract
> >> Boolean Boolean ... -> Boolean
> >> They happen to be "smart" about their arguments, not bothering to   
> >> evaluate the later arguments if the earlier arguments answer the   
> >> question.
> >
> > In fact -> any (including multiple values), for example:
> > (and #t #t (values 1 2)) --> multiple value 1 2
> Right, but my beginning students are using HtDP languages, which
> don't have "values" and require "and" and "or" to take booleans.

Yes, you're using a wrapped version that adds the extra requirement,
this complicates things a little.

> >> Failing that, would it be reasonable to define "and" and "or" in
> >> such a way that if they appear other than in function-call
> >> position, they evaluate to a function that does the right thing,
> >> so again they could be passed around as functions?
> >
> > Something like:
> > [snip]
> Yes, that's pretty much what I had in mind, and that's exactly the
> issue I figured would come up.

It's not just IO side-effects that you should be worried about -- it's
things like this that are more problematic:

  (define (foo x combine)
    (combine (number? x) (< x 5)))
  (bar and "bar")

(Or worse, you can run into an infinite loop, because `and' loses its
power to stop the evaluation.)

> > However it seems pretty messy to me <or> sometimes evaluating all
> > <arguments> and sometimes just as many as needed. Why not stick to
> > and and or as special forms and use regular functions with
> > slightly different names (like andf and orf) where a function is
> > wanted (implying that all their arguments are evaluated)
> Yes, that would be messy.  But is it messier than saying "or" can be
> used in function-call position, but it can't be used AT ALL in
> function-argument position?

But it really cannot be used as a function argument, because it
doesn't have a meaning as a function.  I find that saying something
like this:

  Parens in Scheme mean function application *or* some rule that
  translates a form to another form.  So when you see (and X Y), there
  is really no function that is applied there, it's just something
  that the compiler knows to translate to (if X Y #f).


  `and' is really a piece of syntax that has no meaning by itself just
  like `define' and `cond' (which are things that cannot be a function
  even in a lazy language, for different reasons).

In any case, I think that saying something like that is far better
than the mess of allowing them to be used as functions that kind of do

(Alternatively, you can do the whole course in lazy scheme -- but that
comes with a whole bunch of potential problems for an htdp-like

> For my first-semester, non-CS-major students, it would be nice to be
> able to use "or" and "and" as arguments to "map" or "foldr" or
> something like that.

What would you expect to happen when `foldr' gets `and'?  For example:

  (define (foo x)
    (foldr and #t (list (number? x) (< x 5))))

> In ISLL, they shouldn't run into situations where short-circuit
> evaluation makes a significant difference (unless one of the
> arguments is a function call that goes infinite).  Particularly
> since, by the time you've constructed a "list" of these things in
> order to use "map" or "foldr", all the elements of the list have
> already been evaluated anyway.

But that's exactly the thing that looks confusing to me: in the above
example, you need to explain that the list is completely evaluated
regardless of the function used in `foldr'.  This is similar to
explaining that in the earlier example there's a difference between
using `and', and using another name that happens to be bound to the
same value.  In short, I think that a good summary is either you're
working in a real lazy language, or you have to do these explanations
anyway -- and if you choose explanations, then a "sometimes works, but
sometimes not" rule is much harder to explain.

          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                    http://barzilay.org/                   Maze is Life!

Posted on the users mailing list.