racket-collections icon indicating copy to clipboard operation
racket-collections copied to clipboard

Optional strictness?

Open jackfirth opened this issue 9 years ago • 5 comments

The lazy behavior of the generic map, filter, etc. functions is very useful, but sometimes lazyness isn't something I want. For example, tests, docs examples, and REPL experimentation. It'd be nice to be able to turn the lazyness off. Maybe a parameter, or strict versions of the functions?

> (map add1 (list 1 2 3))
#<stream>
> (map-strict add1 (list 1 2 3))
'(2 3 4)
> (use-strict-sequences!)
> (map add1 (list 1 2 3))
'(2 3 4)

jackfirth avatar Dec 13 '15 01:12 jackfirth

I think the real problem here is that laziness interacts poorly with needing to display values, whether that be in the REPL or otherwise. Correct me if I'm wrong.

In the DrRacket REPL, it would be neat to have an interactive snip that allows forcing a sequence in the GUI, but that's not really a general-purpose solution. Haskell forces the whole list when showing it, but Haskell doesn't have generic sequences. When given an infinite list, Haskell prints until the user breaks.

Clojure is obviously closer to this model, but I'm reluctant to take serious design advice from Clojure. Clojure prints streams like lists, so print forces them. Otherwise they work pretty much just like Racket's streams. Unlike Haskell, if you try to evaluate an expression that produces an infinite stream in the REPL, it doesn't print out anything, it just hangs and spins the CPU at 100%. It takes a nontrivial amount of time to kill execution. Obviously, that's bad.

I'm not sure how to deal with this in textual environments (read: not DrRacket). Other languages seem to be content with just printing things and having bad behavior when things never end. Maybe that's the only feasible approach.

lexi-lambda avatar Dec 13 '15 01:12 lexi-lambda

What about forcing sequences that are known-finite? when printing, and having map, filter, etc. return streams that are known-finite? if all the inputs were finite? This doesn't help with tests like check-equal?, but maybe that's because equal? should force inputs more. This returns false, for example:

(equal? (list 1 2 3) (map values (list 1 2 3)))

jackfirth avatar Dec 13 '15 01:12 jackfirth

I considered using known-finite?, but that doesn't really work since print can't be implemented for generic sequences in general, only on individual data structures. This is still relevant though—we just want a different data structure than racket/stream's streams, and that data structure could check whether or not it's known-finite?. Still, it seems likely that there are plenty of times one would want to see the values inside a sequence that is not known-finite? because the programmer knows it is finite.

Also, I don't think your example should pass. A stream and a list are not the same, even if they contain the same values. In this case, you probably want some concept of sequence equality, not generalized equality, which could have many different interpretations (which is one of the primary issues with the notion of equality in programming, but that's a much bigger conversation).

lexi-lambda avatar Dec 13 '15 01:12 lexi-lambda

An explicit check-sequence-equal? in something like rackunit/collection might be a better for testing. For REPL interactions, maybe the library could provide it's own #%top-interaction that does the known-finite? check for printing. It seems like a lot of complexity for such a small issue though.

jackfirth avatar Dec 13 '15 01:12 jackfirth

I think giving control of printing to the data structures themselves is still the right approach. The only issue is how to handle streams, which are the current data structure of choice when returning arbitrary lazy sequences. This is theoretically also a Racket problem, but obviously it's heavily intertwined with how this library uses streams, specifically.

Maybe a mailing list post on whether or not the printing strategy for streams should be altered is warranted?

lexi-lambda avatar Dec 13 '15 01:12 lexi-lambda