Hacker News new | ask | show | jobs
by twoodfin 4570 days ago
I am a Haskell outsider, but I've heard SPJ say several times that laziness is what enabled/forced Haskell to stay pure. My understanding is that even advanced Haskell programmers struggle with predicting the runtime cost in space and time of the laziness that bought purity. That unpredictability is highly undesirable in a systems language.

There are certainly ways to have purity without laziness, but it's not straightforward for Rust to adopt the Haskell model, I think.

3 comments

It is perfectly feasible for a strict language to be pure, even if most strict languages are as a matter of fact not pure. What (I understand from what) SPJ said is that laziness is completely unworkable without purity, and in this sense laziness forced Haskell to remain pure.
I'm a Haskell newbie, and when I test a program and it shows a laziness-related space leak, I just put strictness annotations on the parameters I suspect of generating large unevaluated thunks. This has always made the issue go away so far.
But, would you not agree that it would be really nice to have separate strict vs. lazy types? It would bloat the syntax a little bit (e.g., Coq needs separate Inductive vs. CoInductive, Fixpoint vs. CoFixpoint keywords) but the benefits in terms of not having to find space leaks via debugging and profiling would be enormous.
There are several libraries that do distinguish between strict and lazy types. containers [1], for example, separates strict Maps and IntMaps.

That said, introducing new syntax to annotate strictness is far more troublesome than separating strict types from lazy types at the module level, where switching between the two is a comment away:

       import qualified Data.Map        as M
    -- import qualified Data.Map.Strict as M
Introducing syntax would make switching between the two far more painful.

[1]: https://hackage.haskell.org/package/containers

When I say sometimes I need strictness, I really mean it. (Without ugly hacks like deepseq, that is, because I am fundamentalistically opposed to hacks.) WHNF does not quite cut it. This is not to say that I dislike laziness. I want first-class support for both strictness and laziness.

When you come to think about it, a separation of strict types and lazy types makes perfect sense. For example, I want to foldl on lists (strict) but foldr on streams (lazy). I do not want to accidentally use the wrong operation on the wrong type. it is tiny details like these which make a type system helpful for guaranteeing correctness and improving performance.

So, if strictness and laziness are both useful and distinct from one another, why not provide both as core language constructs? Why do languages always have to be biased towards one of them?

I think catnaroek has this right.

Neither that nor my original comment is meant to suggest that it would be straightforward for Rust to borrow from Haskell. Just that the post's comments on Haskell seemed to reflect misconceptions.

OTOH, I am not really sure I would want STM in a systems language. It is a little bit too magical, taking into account all the possible retries of a transaction. Perhaps something based on the pi calculus would be a better fit.
I think the magic factor is a key point. Haskell is by design a very high-level language. Things like STM and other "magic monads" allow a lot of stuff to happen under the hood, with guarantees of correctness because of other constraints on the language, but not necessarily a great degree of control of what exactly is happening. Rust, on the other hand, is meant for low-level programming and as such tends to be very explicit about what it's doing at every step. I think Rust and Haskell happen to share some similarities but ultimately they're just not designed for the same purpose. Nothing wrong with learning both :)

Side-note, Idris is a Haskell-inspired pure language which is strict by default. It's designed from the outset to be useful for systems programming, and also has some cool theoretical ideas like dependent types. It might be worth looking into for those who wish Rust was more like Haskell, or vice-versa.

Sadly, AFAIK, Idris does not have linear types out of the box. I am aware that you can prove you are properly releasing your resources using dependent types, so technically speaking there is no loss of expressivity. However, having the compiler do the checking and enforcement automatically is a huge usability win in my book, especially since juggling with the lifetimes of various resources is such a common task in systems programming.

That aside, Idris looks very promising for verified application development.