← Back to context

Comment by fire_lake

12 days ago

Is Haskell pure?

It has exceptions

You can divide by zero

It has unsafe IO primitives

You're right: "pure" is not a well-defined concept. The well-defined concept that describes Haskell's benefits in this regard is "referential transparency". That means that this code

    let x = <definition of x>
    in ... x ... x ... 

(i.e. defining a variable x and then using it some number of times) is equivalent to

    ... <definition of x> ... <definition of x> ...

Seen in the opposite direction (transforming the bottom code to the top code) this means that extracting repeated code is always a valid thing to do. It's not valid in most other languages, and certainly no mainstream ones.

  • Well, technically that isn't true if you use, for example, unsafePerfomIO in the defintion of x. Referential transparency is still a spectrum, just like purity. Haskell is much closer to purity than the vast majority of languages out there.

    Also, even if Haskell were perfectly pure, the fact that it uses lazy evaluation is far more important to actually being able to make use of referential transparency. In a strict language you will still see a massive performance differences if replacing the first version with the second, in the most common cases.

    • > technically that isn't true if you use, for example, unsafePerfomIO in the defintion of x

      Ah, well, regardless of whether it holds in Haskell, referential transparency is a well-defined concept. Purity is not a well-defined concept (at least as far as I know. Please share a formal definition if you have one!). That's primarily what I'm trying to say.

      But I also disagree with your point about unsafePerformIO. In practice, nothing in Haskell violates referential transparency in a significant way. Who knows why? It's an emergent phenomenon that in principle need not have occurred, but in practice it did. Debug.Trace and similar are about the only things that technically violate referential transparency (and they are extremely tame).

      > the fact that it uses lazy evaluation is far more important to actually being able to make use of referential transparency

      Yes, I agree with that.

      2 replies →

It is pure in the same way that Rust is memory safe. That is too say there are a tiny number of exceptions/escape hatches, but they are not meant to be the norm. Every day programming doesn't involve them.

Exceptions aren't impure anyway.

  • Exceptions define an effect. Code with exceptions aren't actually pure in the sense that their return type doesn't fully describe what the code does, so it doesn't just map an input into an output: there is something else going on

    In some pure functional languages, the pure fragment doesn't have exceptions, and you add exceptions as an effect (or as a monad)

    (If you reify the effect with a type like Either or Result, then the code becomes pure: but that's just a monad like said above)

  • Anyway I really like the take that Haskell is pure in the same sense that Rust is safe

    Haskell has impure constructs but sets you up to define referentially transparent abstractions with them, and this is actually valued by the Haskell community

I feel like exceptions where added as a mix of "look we can do that too" and "maybe if so many functions return optional values then it is going to be too much of a pain to use"

In hindsight I think few would now regret not having added them in the first place.

  • > maybe if so many functions return optional values then it is going to be too much of a pain to use

    I strongly believe that there is a point in the PL design space that makes optionals everywhere usable. Maybe Haskell can still be the language that delivers this.

>It has unsafe IO primitives

To be tongue in cheek then it also has the side effect of heating the CPU.