73 comments

[ 2.8 ms ] story [ 133 ms ] thread
Those points aren't exactly true. Haskell does have for and while loops (forM, whileM), they're just a bit more hidden and cumbersome than usual. etc.
1) They're not the same kind of loops though

2) They're not primitives. It's very much possible to build for and while loops from elements in the Haskell standard library, because you have more powerful primitives to work with. Most languages have loops as primitives.

By the time you’re handy with forM and whileM you’re past caring how they got imported!

Regular tail-recursion takes some getting used to coming from an imperative background, it’s arguably the fork point on whether someone is going to stick it out.

So I came to Haskell from Scheme background, so the tail recursion was actually natural to me. In fact, about a month ago I did 2016 Advent of Code in Haskell, and toward the end, I felt like I was abusing tail recursion [1] to write iterative algorithms like breadth-first-search by essentially "repeatedly consing on to the params of tails calls", as Lispers would probably call it.

The whole time I'm wondering if I'm just writing Haskell "with a heavy Scheme accent", since I see others' Haskell code make extensive use of state monads (which I still haven't attempted to understand), and I also found others' using way more of the monadic / applicative operators like "bind", etc than I have.

I found the hard part of Haskell not the iteration, which from tail recursion was completely natural and straightforward, but rather worrying about the efficiency of the "repeatedly consing" part. For things like stacks, the cost is O(1), but for things like Data.Array, I wasn't sure how much shared structure there was; I mean it could totally be copying the entire array every time I "mutate" an element (not really, since it was still sort of "consing" onto the old array and not actually mutating it).

[1] https://github.com/xdavidliu/advent-of-code/blob/main/2016/d...

(comment deleted)
I wanted to love Haskell, but I've come to realise that a language is more than just expressivity and clean abstractions. UX quality of tools, library ecosystem and general support networks are equally important. I know I could spend some spare time to improve these things, but I can't shake the feeling that since nothing seems to have significantly improved in the last twenty years, Haskell might have just missed the boat. FWIW I'm not a particular fan of Go or Rust, but I really enjoy the overall development experience of using those languages.
In recent years the Haskell ecosystem has drastically improved.

With Haskell Language Server (HLS) there's now great autocompletion and inline error reporting for e.g. VSCode and other editors.

We've recently got dot-notation, so you can type `myRecord.myField` instead of `myField myRecord`, which makes Haskell code feel more familiar when switching from other languages.

With IHP we now even have a Haskell version of Rails/Laravel. It's really a superpower to have Haskell's type system combined with the rapid development approach of Rails :) Check it out here: https://ihp.digitallyinduced.com/ (I'm founder of IHP)

Serokell also has a great blog section on people using Haskell in the industry, e.g. recently there was an interview with the CTO of Mercury. https://serokell.io/blog/haskell-in-production

Having autocompletion and and a convenient syntax to access record fields is standard and expected by most developers.

I think if Haskell wants to convince developers to jump ship (and they don't, according to the infamous marketing slogan) there must be a bigger benefit, I don't think better record Syntax cuts it.

> In recent years the Haskell ecosystem has drastically improved.

And has been doing so for 20 years. But in the meantime, the other language ecosystems improve also, and new programmer expectations pop up.

I think it depends on platforms as well. On my Linux system GHCUP has been a godsend. Makes it easy to upgrade GHC + related development tools that are known to work well together with the Haskell Language Server. And with coc.vim (without any additional tweaking), the inline type errors within my editor simplified the development process a lot (no longer using ghcid since).

The last major hurdle I have with the Haskell ecosystem, is the lack of officially supported (and minimally divergent) alternative Preludes.

I think the Haskell ecosystem is good enough, and there is a lot you can do from native UI to web server development. My issue is just getting my head around the complex types you can end up with.

It is hard to opt out when libraries use them. And I gave it a pretty decent shot but it seems like a big hill to climb. Doable but requires a lot of dedication or a full time job in the stack with peers who can help onboard you.

Some things about the tooling have improved over the years. Stack and Stackage did not exist twenty years ago, and I find them indispensable. Libraries like text and optparse-applicative are stable. GHC has advanced to the point where new releases aren’t exciting to me anymore. The base libraries have settled down after things like the applicative-monad fix.

Other things have not changed much. Editor tooling consists of things that appear for awhile and then fall into unmaintenance. I tried using Emacs but its Haskell mode has improved little in years so now I just use Vim. People say the new Cabal-install is better but the UI and docs are littered with indicators that it’s still beta, and some old functionality has been dropped, so I’m afraid to use it. Meanwhile Stack now appears to be in maintenance mode. People rave about Nix but all it makes me do is scratch my head and wonder how it could possibly do anything that’s worth wading through heaps of conflicting docs and blog posts.

Overall though Haskell does what I need it to do. It kind of reminds me of Unix shells: strange in many ways that haven’t been fixed for decades, but still the best at what they do. Rust gets hype now but it doesn’t seem to be a better high-level language. It’s a better systems-programming language.

Haskell will chug along quietly but with some things backsliding while others improve, I don’t expect much net forward progress.

It's fine if haskell is not mainstream, as long as people experiment radical ideas with it.
It's not only the ecosystem. It's a very opinionated language at the core. Problems that are traditionally solved with procedural approaches don't lend themselves to be solved in Haskell, in general.
I do find multiparadigm languages best for actual work. Opinionated languages are really good for teaching a paradigm though. I didn't really learn oo from c++, it wasn't until college forced me to use java. I haven't really learned functional that well yet. I use the patterns in several languages, but I don't think I'll really get it until I sit down and do something non trivial with haskell. Nothing teaches you to use a given tool faster than it being your only tool.

But then I go back and get my full tool bucket for the big projects.

That’s a weird use of the word “opinionated”. Is a hammer opinionated just because it won’t screw nails?
This argument was make-or-break even a few years ago. With LSP (really VSCode funding), all the name-brand languages are monster in tooling.

That doesn’t change the other pragmatic challenges around Haskell, of which there are several.

(comment deleted)
(comment deleted)
Any theories on why Haskell scores so low on stackoverflow's 'most loved' metric? It's only 56.44% which means that 43.56% of the polled Haskell users don't want to use it in the future.

In the same chart you see a few other not-massively-popular languages that have very dedicated user-bases of people who are really into the language (Rust, Elixir, Clojure, Julia) and they understandably score very high on this metric. Why doesn't Haskell?

I actually think it has a lot to do with the runtime and perhaps a little with compilation time.

Compared to the JVM languages I would choose Haskell any time (if given the choice), but for most native tasks the "burden" of Haskell's type system and mediocre performance makes it hard to pass the "value return" threshold compared to Go, Nim, Rust or even C++.

I'm sure that Haskell's performance is plenty for most applications, and the low level/unpure escape hatches are there, but they also raise the complexity significantly from that I have seen.

To me Haskell still has an aura of language research.

This may be a prejudice assumption, I have not done anything more than toys with it.

I don’t know, Haskell and Go absolutely plays in the same field regarding performance from a surface level analysis (and also in benchmarks) - both are compiled to native code with in-built GC, whose usage they can heavily cut back on compared to a traditional OOP language due to strong FP and value types, respectively.

Whether it means you should lower your expectations for Go or raise them for Haskell, is up to you but Haskell can be a quite performant language for many kinds of problems - even if laziness may make performance reasoning harder.

Haskell has a veeery steep learning curve. Most of the learners give up when starting to deal seriously with the type system. It happened to me: I've started to learn Haskell 3 times in the last 6 years. The first two times I gave up after a while. Now I feel I progressed consistently: I am dealing pretty well with notions such as type classes, functors, applicatives, monads, traversables, foldables etc. There are no similar notions in any other language I know about.

It's a pure language, too pure, there are no variable, no state etc.;

It is difficult to motivate people (outside academia) to learn it and use it for something productive.

Moreover, there are other FP languages that are more attractive and fairly easy to learn: Elixir, Closure, Elm. They are not so "pure"...

> It is difficult to motivate people (outside academia) to learn it and use it for something productive.

The main reason I suggest people learn Haskell:

"A language that doesn't affect the way you think about programming, is not worth knowing." – Alan Perlis

Just kidding, I have bills to pay, too. But my standard for whether it's worth learning another langage is quite a bit higher now.

I definitely felt that learning Haskell improved my non-Haskell programming by making me more conscious (and cautious) of the impacts of using state. And more aware in retrospect when that has been the cause of wasted days debugging.

I don’t know, even very basic web development will require using plenty of these concepts, just in an ad-hoc, non-named way.

I feel learning them through Haskell in a way where they have a name and a concrete concept, have proper types and thus compile time errors is so much more easier.

It’s a monster pain in the ass to learn well enough to see the benefits.

The lightbulb moment can be arbitrarily bright (and it is) but if the path to it exceeds readable maximums around pre-return investment you’re going to get modest adoption.

I love the ideas behind functional programming so I assumed that I would love Haskell. However it turned out that I am more productive and happy using C++/C#/Java/Typescript for real world programming. Having said that, I learned a lot from learning Haskell and it made me a better programmer in other languages.
I've never understood the appeal of purely functional languages like Haskell. Mutability is an essential feature of the Universe we live in. Everything changes, internally and externally, all the time. A language that treats mutability like an afterthought (or worse, like a bad thing) is necessarily going to be a poor tool for modeling the world.

Multi-paradigm languages dominate practical software engineering because we live in a multi-paradigm world. High-concept approaches like purely functional and declarative systems that attempt to coerce the programmer into a specific style of problem solving cannot compete when the problem space itself is so diverse.

It is true, however a lot of what we have today in multiparadigm languages originated from niche ones like Haskell. Jazz music had a lot of influence on musicians in other genres, say Pink Floyd, but PF is not exactly jazz.
> Mutability is an essential feature of the Universe we live in. Everything changes, internally and externally, all the time.

That's not obviously true, actually. From a certain standpoint, the Universe IS immutable.

E = mc^2 means that matter and energy are never created or destroyed, just changed from one thing to something else. So if we abstract away the nitty-gritty details of what state it's all in at any given time, the total energy content of the universe is immutable.

More than that, while the passage of time creates an -illusion- of things changing from one state to another, within each instant of Planck Time nothing changes at all. And there's no conclusive evidence that the apparent CORRELATION between each state of the universe at each successive instant and the one that came before it is NECESSARILY a direct CONSEQUENCE of the previous state.

More than that, consider that the past cannot be altered once it has happened. And there is no conclusive evidence that the future is fundamentally different. Furthermore, while we only have access via sense perception to the state of the universe that we call "the present", that's no guarantee that "the present" is the only state of the universe with some sort of existence to it. Maybe the past and the future exist contemporaneously with the present, forever existing as they are without alteration. In that case "Mutability" stops seeming so fundamental after all.

In short, if we conceive of the universe as a data structure with infinite capacity, running on hardware with infinite resources–that is, if God wrote the universe as a purely functional program free of side-effects—them there's no compelling reason at all why that data structure must be mutable.

That you assert mutability as "an essential feature of the Universe we live in" is an artifact of the style of thinking YOU are used to. It's not at all obviously fundamental. That's just the paradigm you're comfortable with.

Haskell is useful, if for no other reason, as a forcing function to make you realize a totally different way of conceptually framing problems, solutions, and the universe that both exist in. In my opinion, it's worth the exercise to grow as a thinker.

Didn't expect to be discussing metaphysics here, but...

> consider that the past cannot be altered once it has happened.

What is the basis of saying this? I would argue this is a cultural assumption not based on any known laws of physics at all. In fact I believe the past (as seen from the present) is quite mutable. We can't go back to the past, and we only have evidence from the present as to what the past was like / might have been. From a cultural / evolutional point of view, we tend to record the events in the past so as to be able to reference it later. You may argue that we can't do the same for the future, but there are trivial ways to ensure the future unfolds in a mostly predictable way -- we just choose not to do that and instead take on risks, preferring the future to be opened ended and preferring to be pleasantly surprised.

> there is no conclusive evidence that the future is fundamentally different

Agree, and I would argue that the past is as deterministic as the future. (If you believe the future is deterministic then the past can be as well, but if the basis of believing the future is so is because you believe the past is deterministic, then I believe you haven't proven that assumption yet).

Thanks for engaging so thoughtfully. I'm enjoying reading and thinking carefully about what you wrote.

Is the past unchangeable once it has happened? I suppose you're correct that I assumed this to be so. I suppose you're also correct to imply that it's an unprovable assumption—as I think about it, it doesn't seem like I can really prove either case (mutable past vs immutable past). But, it seems to me, assuming the past to be immutable is safe for me to do because I will never be "bitten" by that assumption. In other words, even if my assumption "turns out" to be wrong (whatever "turns out" might consist of), I risk nothing by acting as though it's right.

Here's some thought experiments supporting my stance:

1) Suppose that the past can be changed, and that change is guaranteed to have complete and consistent consequences on the present—that is, the flow of time, once changed, will be as though it had been that way all along. In that case, from my perspective in the present, the "updated" present would "always have been the case" and the change would not be detectable. But, I might argue, that being the case, there's really no downside to me operating as though the past cannot be changed. Any conclusions I reach based on that assumption will be internally consistent with the rest of my reasoning and experience of the present, even if the assumption is untrue from a perspective that I cannot observe the timeline from.

2) Or, suppose that the past can be changed, and that change is guaranteed to NOT have consequences on the present. In that case, from the perspective of the present, every indicator that I have of what the past was could be exactly the same (even my memory of it) and yet the past as it "truly" is has been altered, making all of my indicators unreliable. But, I might argue again, since from my perspective the past changing without consequences on the present is indistinguishable from the past never changing, I'm again safe to assume an unchanging past without any difficulties being introduced to my reasoning or experience of the present.

3) Finally, suppose the past can be changed, and that change is NOT guaranteed to have complete and consistent consequences on the present—that is, some detectable artifact of the past being altered could be created in the present. In that case, I will go ahead and make a bald assertion: No artifacts of alteration of the past have ever been identified or will ever be identified. This is a plainly falsifiable assertion. I cannot continue to hold it if it is falsified by evidence to the contrary. But until that point, I'm comfortable accepting it as valid, since it agrees with conventional reasoning about time and, as far as I can tell, is consistent with my experience and the rest of my system of reasoning. In other words, I see no compelling reason not to operate from that assumption as true, even if it is unproven. My system of reasoning has many such positions in it.

So the burden is now on you to prove that the past has indeed changed and show me how you know that, otherwise I'm content to continue assuming as I have that the past cannot be changed (even though I admit, as you've forced me to, that this assumption does not rest on evidence-based proof but on lack of proof otherwise).

So let me now consider your argument that the past is, as you say, "quite mutable." Honestly I'm not sure you've proven that the past IS mutable, just that it COULD BE mutable. And you've told me that you think it is. That isn't compelling enough for me to eliminate my standing assertion(s) above (even though I grant the substance of your challenge).

It occurs to me that there's another angle I could pursue on this, which is to examine what we mean by "time" itself... But I that might be a little beyond my abilities to reason out, so I'd better stop here.

Does Haskell treat mutability as an afterthought? I thought constructs like IORef were for cases in which mutability is needed.
No, mutability was always central to Haskell. Investigating how you could track through the type system and isolate the parts of programs requiring side-effects was the research goal of Haskell. It’s the most notable evolution from Miranda.

Haskell issue is not that mutability is an afterthought. Haskell issue is that Haskell is a good research project but not a very good programming language.

That could be the case. I've never used it. I did "learn" it to use a library (fp-ts) in typescript which basically brings Haskell into typescript and I love the concepts. It's certainly changed the way I think about programming.
It’s common for teams to overuse IORefs or the ST monad when they need mutability, which can make Haskell feel like an imperative language or maybe OCaml.
Haskell is the "Periodic Table of Computation". You really can't advance to "organic chem" levels of complexity without first mapping out the elements and their key properties, and then further super-specializing in the structures of Carbon. Haskell's C/H/N/O is functor/applicative/monad/arrow and these are the elements of data/interpreters/evaluators/compilers. Software engineering is still in the alchemy stage – thousands of king-funded charlatans attempting to transmute lead into gold
Great analogy. Poetical. Actually your first version (before editing) was more poetical but I understand that many of us may be too biased to find it funny: "king-funded javascript charlatans attempting to transmute lead into gold."
I appreciate a good JavaScript dig every time. But in fairness, when I was trying to learn FRP (functional reactive programming, different from React), JavaScript was the only other language where I could find some interesting FRP libraries. There's definitely a lot of exciting JavaScript libraries (and more likely to have documentation than Haskell) you just won't find them used in big corporations.
I'm interested to know what JavaScript libraries you looked at, and maybe some recommendations.
bacon.js, most, and flyd. See also: https://github.com/stoeffel/awesome-frp-js

What was most helpful was the tutorials and explanations these projects had. Haskell projects, if they have documentation at all, are often written for other Haskell users, and so hard for me to understand as a novice.

These JavaScript projects had a unique perspective and a unique way of explaining FRP concepts I found helpful.

I think we can leave out arrows
arrows generalize function composition and capture the essence of a DAG which is a foundational primitive in compilers with applications in garbage collection, structured concurrency and reactive programming
Nice metaphor. But what is the difference between an evaluator and an interpreter?
evaluator is superclass of both interpreter and compiler. monad is interpreter only - as it essentially models continuations, you cannot know what a monadic computation will do without running it whereas compilers are static analysis transforms over a static AST data structure. monad : dynamic :: applicative : static. dynamic means control flow (continuations being the foundational control flow primitive that can express all others aka imperative programming); static means declarative, it’s a data structure (no control flow) which can be inspected and statically analysed and decomposed into components then reassembled, all without evaluating it. applicative permits zero cost abstractions where a declarative high level abstraction is compiled into a target implementation and baked such that the abstraction has no runtime cost. because it has no control flow, applicative also captures parallelism - a data transform can be massively parallelized because there is no control flow / dynamic runtime state to coordinate.
> you cannot know what a monadic computation will do without running it

This is not unique to monads. You cannot in general know what any computation will do without running it. That's the halting problem. Even compilers are subject to this if you have a sufficiently expressive type system (or macros).

the halting problem applies to turing complete computations, which declarative computations are not.
Huh??? The Wikipedia article on declarative programming [1] lists functional programming as a sub-paradigm of declarative programming, and Haskell and Scheme as examples of functional languages. But Haskell and Scheme are obviously Turing-complete.

So I have no idea what you are talking about.

[1] https://en.wikipedia.org/wiki/Declarative_programming

Hmmm. Nice metaphor but it actually doesn’t work. Many of the properties of complex molecules, esp organic ones, are not computable from their atomic structures with current knowledge/computational power. If they were, we wouldn’t need a whole lot of experimental chemistry.
Then why is actual "organic chem" level of complexity (running bio-chemical simulations in Python, C, C++, Fortran, Matlab, Julia, Rust, ...) simulated in different programming languages?

My question is partly serious, as I would imagine a superior programming language to find applications across multiple domains (and Haskell is mostly just used in the Parser/Compiler and web service industry).

I think you've basically hit on the reason: chemistry is hard! Organic chemists would get nowhere if they had to do everything from scratch, deducing the behavior of large complicated hydrocarbons by applying quantum mechanics to the individual atoms. Instead they can just say "I know how benzene behaves", "I know that alcohols act this way", etc. Similarly, when coding, it's nice to have a lot of concepts baked into the language in the form you're going to use them in rather than have to reconstruct them from scratch. For instance, Haskell supports something resembling structs with named fields via "optics" (lenses). But it's a lot to wrap your brain around compared to opening a python repl and typing "x.attr".
Haskell supports structs with named fields out of the box in the form of records.

    data Foo = Foo { attr :: Integer, attr2 :: Integer }
    
    printSomeNumbers = do
        let x = Foo { attr = 3, attr2 = 4 }
        print (attr x)
        let y = x { attr = 7 }
        print (attr x)
It's true that the formatting is different, but given the rest of the language that's not really a problem. What is a problem is how ugly nested record updates are:

    data Bar = Bar { attr3 :: Integer, foo :: Foo }

    setTheThing x i = x { foo = (foo x) { attr = i }}
        
That's... not great, is it? And as you get deeper it gets worse, and it can actually get less efficient to as you keep diving down through structures to grab the pieces you need to reconstruct the unchanged parts of your new copy of the record.

So it's not about "x.attr", but "x.attr.field = 7" and friends.

i didn’t say haskell should be used commercially; imo it should not. I say commercial projects would benefit greatly from a study of computational structure and that is equivalent to a study of Haskell (given that it is exactly a catalogue of computational structure). That knowledge can then inform commercial practices.
>"Software engineering is still in the alchemy stage – thousands of king-funded charlatans attempting to transmute lead into gold"

This is going into to my list of quotes. Thank you. Although I do not think it will ever get out of this "alchemy" stage.

Slightly ironic that it says you don't need variables, then in the example (and without comment) simulates one
Haskell as a research language was very successful. It taught us a lot things:

- Monads work as a way to isolate side-effects;

- Lazy by default is a bad idea;

- Overly academic programming languages generate toxic community.

I’m a long time lisper. Learned Haskell ... a couple of times now. It’s interesting and in some cases fun and elegant. But I just kept coming back to Lisp for actual work. Lisp just seems to be at the sweet spot between functional, extensible, and work-a-day, and the parens just make the refactoring experience so fluid that it’s like a drug. (Edit: I want to learn clojure, which should be easy from Lisp. I’ve avoided it bcs the JVM makes me nauseous. But I’m told by my cj-loving ex-lisp hacker friends that you don’t need the JVM. So maybe I’ll try again.)
Clojure is hands-down the best programming language I've used.

That said, I'm still holding out hope that it'll be hosted on top of something other than the JVM -- frankly, the JVM keeps me from using it more than I otherwise would.

I really love Haskell but I can’t help but think it would be a better language if it was built in s-expressions.
I learnt a bit Haskell many years ago as undergrad student, and would like to do it again. Perhaps it's just my impression, or it's generally true that there's not much efforts popularising Haskell for system programming (OS kernel, embedded devices, microcontroller, etc)?

Maybe Rust (or Zig, or whatever) is better choice for this purpose...

My biggest issue with Haskell is the syntax. Things like the dot operator requiring functions to be chained in reverse from every other language, the ugliness of $ vs using parens, and needing to prefix struct fields to keep them unique. It is terse and fits the language well enough but it is not beautiful.

I am ambivalent about pervasive laziness, lenses, and automatic currying.

I really like controlled effects and think it is one of the most powerful tools for structuring programs.

I've come to appreciate function application / composition syntax and think it allows for much more clear and readable code, compared to e.g. Scala which can be quite verbose and obscure the compositional structure of functional code.

On the other hand, I agree that lack of a good solution for record syntax is a huge wart on the language that regularly gets in the way.

You can use a different combinator if you don't like .

What other languages have a different behavior? Pipes in shell and python work the same way.

(comment deleted)
The problem with most (not all) criticism of Haskell is that most of it is written by people who have not learned FP beyond using it occasionally and inconsistently within a multi-paradigm language, or who have not achieved Haskell fluency.

People find Haskell challenging in novel ways, are frustrated that their java patterns aren’t working, and end up wrongly believing that Haskell is “not practical”. One must learn to conform and go along with the grain, and acquire some threshold of proficiency, before one can judge.

I'd say Haskell is much more challenging that Scheme/OCaml but it's also what makes it interesting. It's very rewarding to understand all the abstractions. I'd recommend anyone interested in programming languages to learn it seriously.

Now, I wonder if it's worth using Haskell for real world projects. People have been using it for all types of things, so it's definitely possible, but the bar of entry is very high and most programmers will be more productive in different languages. Non-experts usually have a hard time maintaining existing codebases, including experienced programmers.

Besides, type safety isn't unique to Haskell. There are other languages more easily approachable which provide strong guarantees as well.

Haskell is one of those things that people either passionately love or hate.

Because of its uncompromising philosophy, programming in Haskell (or in any other purely functional programming language for that matter) has to pay an upfront cost of structuring side-effects. Type-driven development turns out to be a very useful approach to programming for those who have acquired a sufficient degree of proficiency in typed functional programming.

Granted, the world is messy and computational solutions will therefore have to deal with this messiness which often doesn't fit the neatness of purely functional programming right away. However, what I find interesting is that research into abstractions and ways to manage side-effects have led to insights and knowledge and into computing that couldn't have been readily obtained if the strait-jacket of pure functions was not put on. Such abstractions and connections with algebra of computations all help develop tools for building reliable software.

IMHO, the fact that Haskell is actually used in production is somewhat an accident. I think it began with people who programmed in Haskell for fun decided to try it out on solving real-world problems. And then gradually, when monad transformers came around, using Haskell in production became a real option.

I don't think the time commitment to learning Haskell is much more (if at all) than that to learn C++. In fact, after having programmed in C++ for quite a few years, I have pretty much given up on it. Obviously, if one needs to put food on the table, learning Haskell at the expense of other languages du jour would be a mistake. But being able to approach computing from the functional paradigm could be deeply rewarding.