Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No laziness in some vector operation

Tags:

haskell

This code tries to eagerly evaluate [1..] which causes an infinite loop.

import qualified Data.Vector as V

infiniteLoop = V.zipWith (+) (V.fromList [1..4]) (V.fromList [1..])

Why is this so?

like image 380
Evan Sebastian Avatar asked Jul 05 '14 09:07

Evan Sebastian


2 Answers

Compile with -O2.

... ok this only works in certain cases.

In your unoptimised build, the two vectors created via fromList are built first. Since vectors are spine-strict (and unboxed ones hyperstrict), this will fail as you can not construct an infinite-size vector.

If you compile with -O2, stream fusion comes into play. Now, all intermediate vectors (the ones from fromList) are not created at all. Since zipWith stops once the first supply of data is done, you now have a terminating function.

But in general: don't use infinite-size supplies with vector operations, the semantics of your functions now depend on your optimization level, which is bad.

The original "stream fusion" paper describes the switch from lists to streams, and back to lists again. For simplification you can think of lists as vectors (as vectors add a bunch of additional stuff like memory allocation, monadic behaviour, ...).

In general (and much simplified), rewriting rules are used to internally represent vectors as streams, enabling fusion, and streams are then turned back into vectors.

like image 103
choener Avatar answered Sep 19 '22 10:09

choener


Data.Vector.fromList is documented to take O(N) time. Here, you've supplied an infinite number of items, so it takes infinite time to complete.

As for why vectors can't be constructed lazily: they promise good performance for other operations (take, drop, length, indexing...), which requires using a data structure that knows how many elements exist.

like image 38
amalloy Avatar answered Sep 21 '22 10:09

amalloy