Thinking with Lazy Evaluation
begriffs.com
begriffs.com
That's an interesting perspective, I've never seen it that way before. I would love to learn more about this, as a non-Haskeller.
quoted in the paper "Monads are Trees with Grafting" by Dan Piponi
Abstractly your program is a tree rooted at main. In a language like C you concentrate on building trees knowing that you are going to touch every node. So you are trying to design small trees that have good execution semantics as well as being correct in the problem solving domain. You have to address the problem at 2 levels. Because C is strict every function you call will be evaluated before it can return some result.
Haskell is different you can build any tree you like because a node will only be evaluated if its result is required for the current context. You are free to work with models of data like "the integers" or "all possible game states".
Concretely in C if you wanted to find the smallest positive integer that satisfies some property P you would write something like
for (int i=1; ; i++) if (P(i)) return i;
This is a single node in your program tree which modifies a variable until a condition is met.In contrast in Haskell you might do something like
find P [1..]
I would interpret this as find operating on an infinite lazy tree and discarding the infinite tail list (which is an infinite computation) once the result is found.Applying this simple example, in Haskell you aren't concern with limiting the shape of your program's tree. You build the correct model for your program regardless of constraints like infinite lists or terminating searches early. You just allow non-strict evaluation to process your input data because you will never have to strictly generate an infinite integer list before scanning it.
At root non-strict semantics is just a reversal of the order of reduction of expressions.
Beautiful comment btw.
GHC tries to do its best with its strictness analyzer; it looks it works in many simple cases, still blowing up in non-trivial places, producing space leaks. Although I must say that space leaks may be very well produced by a redundant strictness and people who want strictness by default seem a bit luddistic to me.
My ideal language (or rather its compiler/runtime) would do complete analyses of what strategy suits each part of the program. By the way I'm a little surprised by Haskell insistence on static analysis, disregarding tracing VM and automatic run-time profiling.
That's not actually what "non-strict" means. https://wiki.haskell.org/Lazy_vs._non-strict
http://arxiv.org/abs/1504.07680
But I haven't found anything particularly satisfying from the practical point of view.And unix pipes, and node.js streams...
Your intuition about a middle ground is interesting. Perhaps one of javascript's derivative languages could come up with some syntax to make using this more intuitive, because let's face it node.js streams would be easier to use if there were some way of plumbing them together that didn't look like Java from 1999.
I was exposed to them in the Haskell music library Euterpea in the Haskell School of Music [2]. The signal processing API uses arrows.
[1] Generalizing Monads to Arrows. John Hughes. http://www.cse.chalmers.se/~rjmh/Papers/arrows.pdf
You might want to checkout purescript, which I'm pretty sure uses Monads to make stream interaction intuitive. No time for an example, but I believe there is one somewhere in the Purescript book[0].
I thought conduits and io-streams are preferred over pipes.
1. Pipes | Conduit
2. io-streams (perhaps not in 1 only because of a lack of popularity and it is new)
3. Iteratees (have some issue Pipes/Conduit do not which I can't recall)
Not sure where Machines comes in here.