[plt-scheme] about letrec and continuation : which behavior is correct ? and why ?

From: Eli Barzilay (eli at barzilay.org)
Date: Wed Aug 20 22:32:54 EDT 2008

On Aug 20, Michael Vanier wrote:
> Jumping in here...
> 
> I wasn't aware that "letrec" was defined in terms of its let/set!
> semantics (though I knew that it could be so defined).  I had always
> assumed that it was a primitive.  To muddy the waters further, I
> believe that R6RS says that internal defines are actually translated
> into "letrec*", even though I don't know of any Scheme
> implementation that actually supports "letrec*".

At least in PLT, internal definitions were always done with a
`letrec*', or more accurately, mzscheme's `letrec' is actually a
`letrec*'.  IIRC, ages ago there used to be a `letrec*' binding which
was identical to `letrec'.


> It's all a big mess.  I also hate it wrt teaching, because SICP, for
> instance, describes define to have very simple semantics, but this
> isn't at all what internal defines really do (I'm guessing that the
> reason for this is that implementing internal defines in terms of
> some letrec or let/set! primitives is easier to optimize).  So users
> expect that internal defines behave in ways similar to top-level
> defines, which they do not.  This significantly undercuts the
> argument that Scheme is a "simple, elegant" language.  I don't know
> of a good way out, either.

It's still pretty simple.  Continuations have a bad habit of exposing
all kinds of implementation details as happened at the beginning of
this thread.


> I kind of like the Ocaml model where there are exactly two let
> forms, "let" (which makes one binding only) and "let rec" (which
> makes any number of mutually-recursive bindings).  However, the
> syntax would be awkward for Scheme, and even in Ocaml, top-level
> lets behave differently (there is an implicit "in" which scopes over
> the rest of the file).  I don't know if Ocaml resolves "let rec" to
> let/set!, but I doubt it.

It doesn't matter, because there are no first class continuation, (and
because of the restriction on what you can put in the rhs of
`letrec's).  And BTW, OCaml's `let rec' is still inconvenient, it
often forces you to put the whole file in a single such form -- if
there's a function at the top that needs to call a function at the
bottom.

And personally I found that to be very disturbing, since I'd try to
move just the calling function down, but that might drag others too.
In general you get a general topological sort problem to solve, and if
in the end you find that you really need mutually recursive functions
then either you work to restore the reasonable order and wrap it all
in a `let rec' or you leave things in the mess they happend to be left
at.  Of course you could try to always begin with a single `let rec'
for everything, but then you're stuck with polymorphic functions that
don't work...

Finally, there's the fact that because of the implicit `in' that wraps
the rest of the REPL too, you get the well expected mess:

  # let x = 123;;
  val x : int = 123
  # let foo() = x;;
  val foo : unit -> int = <fun>
  # let x = 1000;;
  val x : int = 1000
  # foo();;
  - : int = 123

and this mess can become a major pain when you're dealing with new
type definitions.  (My personal conclusion was to not use the repl for
more than very simple games.)

The bottom line is that it's not all roses in the ocaml world either.
Bindings are difficult any way you look at it.


> One other gripe: I've read in some places that PLT Scheme has about
> 12 fundamental special forms, but I can't find any documentation
> about which forms they are.  Is that an oversight or intentional?
> I'm sure there is no desire to set Scheme primitives in stone, but
> it would be nice to know what forms an expression will eventually
> expand into.

This might be what you're looking for:

  (require syntax/kerncase)
  (map syntax-e (kernel-form-identifier-list))
  -> (begin
      define-values
      define-syntaxes
      define-values-for-syntax
      set!
      let-values
      letrec-values
      #%plain-lambda
      case-lambda
      if
      quote
      letrec-syntaxes+values
      with-continuation-mark
      #%expression
      #%plain-app
      #%top
      #%datum
      #%variable-reference)

but it's useful in rare cases.

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


Posted on the users mailing list.