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

From: Eli Barzilay (eli at barzilay.org)
Date: Thu Aug 21 21:35:19 EDT 2008

On Aug 21, Joe Marshall wrote:
> Ok, I get what you're saying.  You want circularity in the data
> space as well as in the function space and letrec does that.

Yes.  (And that's why I like the `letrec*' semantics -- it give me
some semi-sane way to mix non-function values, except without the
self-referencing bit.)

> I'm guessing, however, that you want EQ-ness as well.

Only sometimes.  In the examples I gave, eq-ness was important to be
able to actually get the (cyclic) list.  If I stick to the lazy
language, then I could define `cycle' in a much simpler way:

  (define (cycle l) (append l (cycle l)))

But this won't work if you want to get a hold of the list in plain
Scheme -- there's no (easy/efficient) way to know when you have a
loop, which is what you'll need to make the inner call into a cycle
pointer.  So I used a version that had an explicit cycle via a
`letrec' value (in the lazy sense).

> I don't understand why you want circularity, though.  If you don't
> intend on using side effects, then there's no way, other than via
> EQ, to determine if a structure is circular.  And if you don't
> intend to use side effects, then a non-circular, but infinite
> structure would be observationally equivalent for every function
> *except* EQ.  In other words, you couldn't write a program that
> would return a different answer for circular vs. infinite *unless*
> you explicitly used EQ.

Well, it's just the above -- I don't care much for `eq?' in itself.
If you manage to solve the forcing-an-infinite-lazy-value problem
(which is the same as, for example, writing such a structure to a
file), and if you gave me `equal?' that was as efficient as `eq?',
then I probably wouldn't even notice when you take away `eq?'.

> (And depending on the philosophy of the implementor, a `pure'
> implementation of Scheme could use hash-consing to fold equivalent
> structure and the circular and infinite structure would be
> indistinguishable.  Baker argues for this in
> http://home.pipeline.com/~hbaker1/ObjectIdentity.html )

Not in the above code.  The actual representation has promises holding
cons cells to more promises, and the promises themselves cannot be
unified unless you have a way to prove that two functions are
equivalent.  (Which might be possible in some simple cases like the
above, but probably too expensive to detect automatically.)

> So either you are using letrec because
>   1)  You want infinite, self-similar objects and you don't care
>        about `circularity' per-se,
>   2)  You really want *circular* objects because you intend to
>        do something that can distinguish between truly circular
>        objects and inifinite ones.  This implies the intent of
>        side effects.
> The last paragraph where you say `` I don't really care if Matthew
> made `letrec' achieve the magic by a `let'+`set!', by a fixpoint
> combinator, or by voodoo'' seems to indicate the former.

Yes, it's the former, mostly -- but a usable version of it.

> If *that's* the case, we don't have much of a problem.  Abdulaziz
> Ghuloum's example:
>  (letrec ([x (list (lambda () x))])
>    (eq? x ((car x))))
> would simply be allowed to return #T or #F depending on the
> implementation.  I could definitely live with that, but I expect
> other people will balk at it.

I'd balk at it too -- *unless* you give me this:

  (letrec ([x (list (lambda () x))])
    (equal? x ((car x))))
  --> #t

instead.  Since I can't get that, I need `eq?'.  So I guess that I'm
saying that the side-effect that is (possibly) used to create that
self reference is an unfortunate by-product of working in an eager
language -- which is one more reason I don't want to do it explicitly.

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

Posted on the users mailing list.