Should Haskell be rebranded away from "Pure Functional Programming"

13 posts were split to a new topic: What is a good formal definition of purity?

What a surprise: it’s a question about I/O in Haskell - I’m assuming none of these helped. Let’s try something different and see how Agda performs I/O:

Haskell 2010 almost permits a similar "outsourcing" of I/O:

-- the I/O ADT...
data IO a
foreign import "primUnitIO" unitIO :: a -> IO a
foreign import "primBindIO" bindIO :: IO a -> (a -> IO b) -> IO b

instance Monad IO where
    return  = unitIO 
    (>>=)   = bindIO 

Main.main can be dealt with in similar fashion, so:

module Main(main) where

main :: IO ()
        ⋮

would be translated into:

module Main() where
foreign export "hs_Main_main" main :: IO ()

main :: IO ()
        ⋮

So after all that, what does evaluating main entail?

{- Haskell I/O outsourced: nothing to see here... -}

…just as Agda outsources I/O - and not a real faux world in sight! It just requires some FFI extensions. It would also help to make abundantly clear that the monadic interface is actually relatively boring e.g:

 -- "alternate" I/O ADT!
data IO a
type Await a = IO a -> a
foreign import "primAsyncIO" asyncIO :: ((forall a . Await a) -> b) -> IO b

instance Monad IO where
    return x = asyncIO $ \ await -> x
    m >>= k  = asyncIO $ \ await -> let !x = await m in
                                    let !y = await (k x) in
                                    y

I don’t want to delve into the theoretical intricacies of CS concepts, but I think this entire thread started on the wrong (semantic) foot: to say that Haskell is pure simply means that most Haskell functions do most of their work consuming/producing all and only the values instantiating the types mentioned in their signature. Exceptions, UnsafeIO, Traces and other niceties provided by the runtime are, of course, not (always) part of these.

Most functions… most of their work… that’s a tell-tale indicator that purity is, in fact, a concept that admits of degrees, and whose meaning is to be determined against a host of background conditions (just like, for example, the use of the word “pure” in chemistry that means something like “made of only one substance”; in this context there are background conditions meant to specify, among other things, the “scale” at which substance is individuated, so as to not include, say, exchanges of subatomic particles with other parts of the system within which the allegedly pure substance is considered).

For this reason I think it makes completely sense to not drop “pure” in “Haskell is a pure, lazy, functional programming language” simply because once we specify the relevant background conditions, one of two things:

  • either there is no other language which, relative to this specification of the background conditions, will be considered to be pure in the first place; or
  • Haskell will come out as part of the first-tier of all contenders ordered by purity, relative to this specification.

But my main argument for keeping “pure” is because it gives a super nice vibe to the slogan, a vibe that resonates with the ego craving for virtue-signalling opportunities of many folks, and I think it is not necessary to deprive them from this pleasure.

2 Likes

In my pedestrian view Haskell being pure is demonstrated by the following snippet:

mainLines :: [IO ()]
mainLines = [ putStrLn "hi", putStrLn "see you at runtime" ]

main = print $ length mainLines -- XXX: no, you wouldn't

Sure, in other languages you can wrap that into lambdas or whatever so the effects wouldn’t be triggered. But in Haskell “IO” (the concept, not the type) is just an ordinary value you pass around. And so are other effects. You have to entangle yourself explicitly with the effect to reach for its “result”.

Well … ever tried to talk about “total functions” in a german user-group? Raises some eyebrows :laughing:

1 Like

The key term is “rebranded”, i.e, the notion of branding.

The objection to the term pure isn’t simply that it isn’t true in some sense, but that it implies a lack of pragmatism, first, when Haskell is reputed to be very effective in industry settings, and second, it implies a moralism that can make Haskell offensive to others or more difficult to learn.

In Scala-land, for instance, I’m told that many shops write their Scala as “a better Java”, instead of as an FP / OOP hybrid. This is fine; Scala has multiple times the adoption of Haskell, and eventually Scalators move toward greater FP and even to Haskell (the Hascalator).

Haskell, in theory, can just function as a better C / Python (and there are people who use Haskell as a scripting / glue language). The ST monad exists, so does IORef, etc. Now, this should sound horrifying to all true Haskellers, but in reality, I’ve read that there’s a 3-month onboarding experience at many shops in order for a non-Haskeller to be prepared to work on a Haskell codebase.

The high cost of on-boarding makes Haskell unattractive to businesses simply because you’re putting a person up for 3 months while you get zero productivity out of it. Treating Haskell as simply pure means that there’s no way around it, but if you’re willing to break purity, you can potentially put the trainees on imperative Haskell / Haskell gluecode while you train them, thus getting productivity out of Haskell trainees.

But as long as Haskell is marketed as pure, as opposed to “with purely functional semantics” using a different top-level label, people are less likely to try this, because purity is not merely a language trait, it’s also a matter of language culture.

===

And once again, remember that I’m not technically violating “avoid $ success at all costs”. This is about attitudes and branding, not the language compromising itself to support (more than it already has) imperative or OOP constructs.

2 Likes

Can you give an example of what you mean by “break purity”?

I disagree with:

  • “implies a lack of pragmatism”
  • “implies a moralism”
  • can just function as a better C

But that’s not very important. The more important objectionable bit is this inference:

  • treating Haskell as simply pure means that there is no way around [purity], which is one important factor contributing to difficulties that shops run into when taking Haskell trainees.

I think think it’s a very exotic claim to say the least, that Haskell’s branding in terms of purity makes the life of trainees / shop more difficult – borderline outlandish in fact. The more reasonable claim in the vicinity was perhaps that Haskell’s purity might require some adjustment in the programming habits of development teams if they are only familiar with more mainstream approaches, but I don’t think this is what you wanted to say because it has nothing to do with branding.

But Okay, in the spirit of making something useful out of this, how about reaching out to the HF to survey shops that did take Haskell trainees, and ask them if the branding made things more difficult? From there you could extract a profile of Haskell shops sometimes taking trainees and perhaps even get in touch with shops that don’t take trainees, so as to have a contrastive case for the stats. Then you can back up your claim that branding is misguided.

If the request is to reach out to HF, it might simply make more sense to ask the HF to survey companies that considered using Haskell, and from companies that both considered and selected Haskell, as well as companies that considered and rejected Haskell, to determine what is the most effective branding for Haskell that is still honest.

This would be an enlargement in scope from the simple “Haskell is misbranded as pure” notion that I’m playing around with right now to a more comprehensive “Haskell is probably misbranded” notion.

And we can generalize further from that; i.e, beyond Haskell simply being misbranded, what misconceptions about Haskell do people have that prevent its use in production? How can we ameliorate these misconceptions?

===

Thinking in this vein, one big opportunity is probably “do Notation”, in the context of “purely functional semantics”. An advertisement for do Notation would simply be “get the benefits of functional programming while having your code look traditional / imperative!” And given that do Notation ultimately desugars to a series of binds, this isn’t false.

1 Like

My suggestion was to try to turn the “your playing around with a (technically complex) notion” into a valuable action item. But whatever you do, please have common sense; it does not make sense to call for a rebrand (practical task with lots of implications) if in fact you’re mostly playing around with a notion; and if you are serious about a rebrand, do bring forth evidence backing up your claims.

I’m calling for a brand analysis at this point; i.e, does the brand serve the Haskell Foundation and the Haskell community’s needs? In what ways does it do so? In what ways is it lacking? In the event that it’s lacking, how might adjusting the brand better serve HF / Haskellers?

Yeah well 99% of folks around here are volunteers and you just don’t “summon up” volunteers to do work. Instead you create something like a working group that you yourself are going to lead and you try to build a meaningful, actionable project out of it. If you do it well and with a bit of luck others may want to merge in turn and contribute. Else you move on.

Well, I apologize for being too presumptuous, but I was just responding to your comments that “pure branding is not a problem”. It does make sense, however, to research Haskell’s image among its users and potential users, and see to what extent it impacts its selection choice as a programming language. If you want me to conduct informal surveys, I can try to do so.

Before you do, try reading A History of Haskell first - it will provide some context (and maybe inspiration) for what you’re contemplating.

I agree in a sense but I think it should be in regards to functional programming as a whole. When you say “there are no mutable variables, no side effects and no loops” people think “then you can’t do anything” which is correct, which is why we actually don’t have these restrictions. It confused me a lot in the beginning and made things harder because whenever I would see these things I would think “something is wrong because I’ve heard these are not allowed so whatever this is, it isn’t functional programming”.

I think I like the Robert Martin put it: Functional programming impose discipline on mutation and side effects the same way structured programming imposed discipline on control flow or how OO imposes discipline on state. (I don’t know if the last one is fair but I suspect I’m incapable of being fair to OO)

2 Likes

Having skimmed through the thread, I am getting reminded how helpful it was to think of Haskell as purely functional programming—back when I was still a beginner.

I knew I was learning something that other programming languages didn’t offer, regardless of the fact that they might implement functional programming features. The purity of Haskell accounts for both, the difficulties I had in the beginning and (part of) the power that unfolds now that I am more proficient.

I’d also advocate for more wiggle room regarding the question of Haskell’s actual purity. In this blogpost, Gabriel Gonzalez writes about an important characteristic of Haskell:

The degree to which Haskell encourages the programmer to write pure code (even though this is at its core a subjective measure) is more important than the fact that, in principle you can (and will) write imperative code in Haskell.

3 Likes

You just put pure and imperative as opposites. That doesn’t seem right. There are pure imperative languages too: https://mars-lang.appspot.com/ (although they might not be using Amr Sabry’s definition).

The main thing, as you say, is reasoning about effects. And I’m not convinced Haskell makes this as nice as it could be. There’s a huge design space, but the language just gives you IO and since then we’ve had a bazillion approaches of “extensible effects”, which are either overcomplicated or just inefficient (I’ll skip the discussion about non-termination, I think F* has an interesting approach there).

So it seems the main thing might be not being able to call IO functions from non-IO functions. I think that’s nice, but it wouldn’t be enough for me to be convinced of the superiority of a language (Rust had this too and they deemed it not useful enough and removed it from the language).

1 Like

…and all the while Clean just keeps on chugging along with it’s uniqueness types. Of course Haskell GHC is now so complicated sophisticated that e.g. Clean’s *World type can be mocked up emulated:

module Data.World (World, withWorld, runIO) where
import Control.Monad (liftM2)
import Data.IORef (IORef, newIORef, writeIORef, atomicModifyIORef)
import Foreign.Marshal.Unsafe (unsafeLocalState)

withWorld :: (World -> (a, World)) -> IO a
withWorld ff = allNew >>= \ w -> case ff w of (x, w') -> theEnd w' >> return x

runIO :: Eq a => IO a -> World -> (a, World)
runIO m w = unsafeLocalState (theEnd w >> liftM2 (,) m allNew)

-- Private definitions --
--
data World = World (IORef World)

allNew :: IO World
allNew = do r <- newIORef (error "New world awaits")
            let w = World r
            writeIORef r w
            return w

theEnd :: World -> IO ()
theEnd (World r) =
    atomicModifyIORef r (\ (World _) -> (error "Old world found.", ()))

…just as long as those extra runtime checks don’t bother you.

What is F*? Did you mean to write F#?

https://www.fstar-lang.org/