I’m a skeptic. I don’t believe, I challenge. I’m gonna test my socks off.
Update, first test results
- This appears to work much better now:
At least, it didn’t crash for a = 987654321, b = 0
. (overnight test)
Stack size
is stable at 4. Frames
will still accumulate, at a rate of 33 per iteration (but then, I don’t know the definition of this indicator).
- This apparently works, too: (though I didn’t test it with really large numbers)
- By contrast, the block below crashes (stack overflow):
I would have expected the latter block to work, too, as both it and (1) are equivalent to Scheme:
(if (= a 0) b (recursive-sum (- a 1) (+ b 1)))
, or so I think.
So, apparently, Snap! ‘s tail call optimization only works under strict (even syntax) conditions. But, as far as I can see, it works now.
The next block crashes, like it was to be expected (not a proper tail call, I don’t see how it might have worked for you (bh)):
First tests with streams failed.
I’ll see if I can make that work.
Why with the old keep-stream
(I suppose) ?
And did you try any stream operation yet?
Another update
I’ve tried a lot of things, even reconstructed streams-according-to-SICP, but haven’t been able to make large streams (hundreds of thousands of items) work, with either library version: Snap! keeps crashing. And yes, I used the dev version.
Yet another update
For comparison I tried my hand at programming streams in Scheme. I used LispPad Go 2.01 (a free iOS app claiming R7RS-compliance - BTW @callietastrophic: this is a nice app if you would like to program SICP examples, it took me perhaps an hour of experimenting to figure out how to write and store / retrieve Scheme code, which I never did before). It, too, crashes with some “larger” stream operations, e.g. :
(stream-cdr (stream-keep mod-fifty-k? (stream-numbers-from 1)))
with:
(define (stream-car s) (car s))
(define (stream-cdr s) (force (cdr s)))
(define the-empty-stream '())
(define (is-stream-empty? s) (eq? s the-empty-stream))
(define (stream-numbers-from b)
_(cons b (
delay
(stream-numbers-from (+ b 1)))))
(define (stream-keep pred s)
_(if (is-stream-empty? s) the-empty-stream
_ _if (pred (stream-car s))
_ _ _(cons (stream-car s)
_ _ _ _(
delay
(stream-keep pred (stream-cdr s))))
_ _ _(stream-keep pred (stream-cdr s)))))
(define (mod-fifty-k? x) (= (modulo x 50000) 0))
LispPad Go will successfully process some other large stream operations, though. What I mean to say is, other Scheme implementations (at least this one) aren’t perfect either.
You may have noticed I didn’t use a cons-stream
function. I had written one at first:
(define (cons-stream head tail) (cons head (delay tail)))
… but that didn’t work (probably it should have been a macro; haven’t figured out macros yet) - instead I’ve since directly called cons head (delay tail)
.
Of course I tried something equivalent in Snap!, but that didn’t change anything (which makes sense because Snap! has (limited) argument typing and can thus do things Scheme can’t; bh’s been hinting at introducing “hygienic macros” in Snap!, I wonder what that would add at all).