Should Haskell be rebranded away from "Pure Functional Programming"

Here’s the thing, Haskell isn’t actually “pure (functional programming)”. It has support for procedural-looking code (do notation), actually mutable variables (IORef, STRef, MVar, TVar), as well as dynamic types (Dynamic, -fdefer-type-errors), and even objects, via libraries like Hoop and Objective.

What it is, instead, because of how Haskell relies on the IO monad, and GHC relies on State# RealWorld a, is “pure functional $ programming”. What you end up with is a relatively strict isolation between pure parts of your program and impure parts, which in combination with a syntax highly conducive to FP, permit what is sometimes described as algebraic programming, wherein equational reasoning holds.

The problem, though, is that the term pure creates confusion. Both Richard Bird (may he rest in peace) and SPJ have called Haskell “Imperative Functional Programming”. An astute Haskell learner, after jumping through the initial hoops, will realize that Haskell is capable of producing extremely imperative code (I have an allergy to do notation for this reason and use it very sparingly as a consequence).

Perhaps rebranding Haskell as, instead of “purely functional programming”, “algebraic programming”, “algebraic functional programming”, or “purely functional or algebraic programming” might help to avoid the confusion? The trade-offs would be that the line “pure, functional” helps attract people interested in FP for FP’s sake, but how many of them actually stay here? But in a corporate environment, explaining Haskell as “algebraic functional programming” might avoid the notion of purity and (implicitly) impracticality, as well as provide an opportunity to explain Haskell’s IO monad in terms of its benefits in program design.

The point isn’t to abandon the effective “Avoid $ SuccessAtAllCosts” philosophy, because that’s a philosophy of the GHC Committee. The language Haskell will remain Haskell, not compromising simply to seek adoption. But the branding of Haskell might be improvable.

2 Likes

See also: Conal Elliott » The C language is purely functional

Although, I’ll admit that I am not completely sure what the conclusion of that blog post is. Is Conal Elliot saying we should actually call C a pure language, or that we should call Haskell impure, or that we should keep the status quo because the alternatives are absurd?

Personally, I think I’d prefer keeping the status quo. Calling C pure or calling Haskell impure would make the word “pure” lose all its meaning (outside of tiny experimental programming languages).

Of course marketing the language as pure doesn’t make that much sense, because newcomers are probably not aware of the implications of purity. Instead we should focus on those implications, like making code easier to reason about in isolation and ease of refactoring.

1 Like

procedural-looking, but not procedural.

Back on topic: I suspect we need less propaganda, and more useful programs written in Haskell, like git-annex, hledger and friends.

10 Likes

Haskell isn’t singular - there’s Clean and Miranda(R) and more recently R: I’m now wondering if any of them are also contemplating a changing of their “marketing” nomenclature.

Back in 1987, Haskell was relatively easily to recognise as being pure (embarrassingly so, apparently) - that was then, this is now and Haskell has evolved (yes, acquiring some features known to cause disagreement). The Haskell community is largely interested in programming, not marketing so the “front-page” description hasn’t changed much, if at all.

Yes, the use of the term “pure” (apart from being rather pejorative) does over-extend the dictionary definition somewhat, but so far no more descriptive term has so far appeared. As for “algebraic”…that could scare off potential new Haskellers more than “pure” ever did.

Haskell is unique in many ways and has acquired the greatest “mind’s share” of research and development in this space, so it will obviously be "first with features" (and problems) - that the description hasn’t kept up isn’t that much of a surprise.

…who knows: maybe a better description for Haskell might be found in that process.

3 Likes

I have always found it uneasy to face/state the term “pure”. It gives off this… strange… sense of medieval wicked notion of morale. Why should we call it “Pure FP” when simply “FP” would suffice? Could say “Typed FP” or even “Rigorous (Typed) FP” if one wants to further the accuracy.

Oh. By the way, we have laziness. Which comes with another comparable negative connotation. I do not know how to feel about this one, though. Many dislike inherent laziness of haskell, after all…

3 Likes

Most FP languages don’t enforce purity: Scala, F#, OCaml, Clojure, etc.

4 Likes

Do we need this distinction for common nomenclature, though? I personally find such distinction needlessly discriminating if used often.

Btw, Is the notion “Rigorous FP” bad?

But Haskell is pure: When you say something like

recipe :: FilePath -> IO String
recipe path = do
  ... getDirectory ...
  ... readFile ...
  ... other stuff ...

calling recipe doesn’t actually do anything. Instead, recipe returns an IO action, to be executed at a later time – a recipe. Accordingly, evaluating main does a whole lot of pure work, building up an elaborate recipe for what would happen at runtime, but evaluating main doesn’t actually execute this recipe. The Haskell runtime will, after evaluating main, then actually perform the actions specified there.

So I think calling Haskell pure is technically accurate and would argue against shying away from this – it’s (for me) the chief quality that makes Haskell awesome.

On the other hand, as raised in this thread, I too have sometimes been uncomfortable with the choice of the word “pure” for the concept of side-effect-free. “pure” has too many moralistic connotations. But I think that ship has sailed, given that this word has this meaning quite widely. (For example, the Java API documentation describes some stream operations as working only on pure function objects.)

10 Likes

calling recipe doesn’t actually do anything

Can you state formally what that means?

evaluating main does a whole lot of pure work, building up an elaborate recipe for what would happen at runtime, but evaluating main doesn’t actually execute this recipe. The Haskell runtime will, after evaluating main, then actually perform the actions specified there.

Could you describe in more detail how to apply this to

recipe :: FilePath -> IO String
recipe path = do
   b <- someCondition path
   if b then someFunction path else pure ""

main :: IO ()
main = recipe someFilePath

For example, what does evaluating main entail? Presumably it doesn’t involve performing the actions of the someCondition “subrecipe” (since that would not be pure) and therefore "evaluating recipe" can’t be “building up an elaborate recipe” for someFunction. In this case the following must happen in this order:

  • build up recipe for someCondition
  • actually perform the actions for someCondition
  • build up recipe for someFunction
  • actually perform the actions for someFunction

I can only conclude that “building up an elaborate recipe” and “actually performing the actions specified there” must be tightly intertwined, so they are not two distinct stages! Do you agree?

1 Like

Yes: evaluating recipe x (for any x) to WHNF causes no side effects.

main     =
recipe someFilePath     =
someCondition someFilePath >>= \b -> if b then someFunction someFilePath else pure ""      =
bindIO (someCondition someFilePath) (\b -> if b then someFunction someFilePath else pure "")    =
IO (\ s -> case (unIO (someCondition someFilePath)) s of (# new_s, a #) -> unIO ((\b -> if b then someFunction someFilePath else pure "") a) new_s)

which is a value. This value can be passed around, duplicated, evaluated redundantly again, etc., with no side effects, at all, occurring. It’s only when the function contained in the value is called – which requires a State# RealWorld – that any side effects happen, but this is beyond the bounds of normal Haskell code. (You can, of course, fake a State# RealWorld and get Haskell to behave non-purely, but you can also do that with unsafePerformIO. I’ve implicitly assumed that we’re avoiding such tricks.)

I see what you’re getting at with “intertwined”, but I don’t find it all that helpful: all I’m saying is that evaluating recipe (or main even) is a pure operation that causes no side effects.

4 Likes

I like to talk about referential transparency more than pure/impure. In Haskell writing x = y means that x and y are interchangeable. That’s not true for most other languages. For example in C:

#include <stdio.h>

int increment(int x) {
    printf("value was %d\n", x);
    return x + 1;
}
int main(void) {
    int x = 1;
    printf("y is: %d\n", increment(x));
    return increment(x);
}
int main(void) {
    int x = 1;
    int y = increment(x);
    printf("y is: %d\n", y);
    return y;
}

The two main functions do not behave the same way and that’s unfortunate. Haskell will always let you do this kind of transformation fearlessly and the behaviour will stay the same.

4 Likes

That’s just replacing one undefined (in this discussion) concept with another! What does it mean to “cause no side effects”?

1 Like

Yes! This is where I am hoping my dialogue with Richard will lead …

(And given that you appealed to “the Java API documentation describes some stream operations as working only on pure function objects”, a definition of purity in terms of WHNF or RealWord# seems insufficiently general.)

Every system has to define its own meaning of “side effect”. For example, is the heating up of a processor a side effect? What about consumption of finite memory? We usually say “no”. I don’t feel able to give a complete list of side effects, but it includes things like I/O and reading the time of day. I’m not really sure how to do better here, without writing down a full formal specification of an operating computer (including e.g. the temperature of components) and then an equivalence relation on that specification, where a pure computation leaves the computer in a state equivalent to the one it was in previously.

The problem is, Haskell implicitly has support for multi-paradigm programming via both do notation, object libraries, and even logic programming via monads. Pure in the sense that the core language is functional, and that multi-paradigm programming is done through a functional programming layer, creates some level of confusion, and makes Haskell not seem “pragmatic”, when it is very pragmatic, it just avoids compromising by providing first-class support for multi-paradigm programming and resorts to different hacks to preserve its non-strict and functional-first nature.

1 Like

There are a couple of sides to the issue

  1. The technical definition of “pure”. That’s what Richard, gilmi and I have been discussing.
  2. Regardless of the technical definition, the impression it gives to the outside world. I think this is the main thrust of your original post, William.

I agree with you, William. In so many online discussions in general programming fora I see comments of the following sort:

Haskell is a pure language, that means it can’t have state or side effects, but modifying state and causing side effects are essential in a real-world system, therefore Haskell is useless in the real world.

My mind always boggled how such commenters could believe what they are saying. Do massive corporations like Facebook and Standard Chartered really use Haskell despite it being useless in the real world?! But the point remains: people do seem to think this.

I’m with gilmi and (I think, you): not only does “pure” (in the programming language sense, let alone the moral sense) give the wrong connotations, it’s not even what we value at all! We value referential transparency. The notion of “purity” is a poorly-defined approximation to referential transparency.

Here’s a challenge to folks who like the notion that “Haskell is a pure language”: please explain what benefit you get from Haskell’s “purity” that is not also a benefit of Haskell’s referential transparency.

3 Likes

@tomjaguarpaw, is the following URL yours?

http://h2.jaguarpaw.co.uk/posts/impure-lazy-language

Yes!!!

[Exclamation marks added because Discourse requires that posts must be at least 20 characters]

[EDIT: intriguingly, although I wrote 17 exclamation marks, Discourse only shows 3]

3 Likes

Honestly, I think we could save pure by saying that Haskell is a language with “purely functional semantics”, then pick some kind of marketing buzzword to refer to referential transparency or a pure-impure separation. Purely functional semantics → is what Haskell actually is, since do notation etc is just syntax that converts down to functional code.

The idea is, for people who understand what purity means, purely functional semantics, or a similar term, is equivalent to purity, whereas for bean-counters / language warriors, they have no bleeding clue what it means. It’s a dog-whistle, except for academics instead of reprobates.

So what we’d do would be to describe Haskell as a ___ language, with purely functional semantics, a very strong type system, and non-strict evaluation.

On the top level, then, we find some other term to describe Haskell, its benefits, and utility. Because, after all, the language designers were busier working on the language design than the marketing (see: “Cute, fuzzy things”). But that’s what Haskell Foundation is for, right? HF handles the business and marketing side, GHC Committee handles the language itself.

===

Yeah, and I’m skirting the issue. Referential transparency is a good academic term, but a bad marketing term. If you were Sun, preparing to blow hundreds of millions on Java’s marketing budget, and were all of a sudden forced to blow hundreds of millions on Haskell’s marketing budget, how would you describe Haskell?

1 Like