238 comments

[ 0.28 ms ] story [ 130 ms ] thread
I initially tried to learn Haskell by creating a small Physics programming project. It was fun, and I found some great math libraries, but eventually got frustrated by the slow pace and switched to Python. Specifically, the amount of time I spent debugging types and signatures was really frustrating, and often left me googling the same error patterns to figure out where my mistake might be.

I'm excited to try out Dhall and see if it has a smoother onramp.

I love Dhall and have used it in production, but I will say that the documentation, especially for things like dhall-kubernetes, often leaves much to be desired. You may need to spend a bit of time reading the source code.
My experience has been similar. I found that Haskell struggles, from a usability perspective, when there isn’t a variety of diverse types in your program and/or your programs, by their nature, rely on the characteristics or behavior of machine types. It’s not impossible, of course. It just adds an unfortunate amount of boilerplate.
> the amount of time I spent debugging types and signatures was really frustrating, and often left me googling the same error patterns to figure out where my mistake might be.

In my experience, people often describe their first experience with expressive static type systems in terms of "fighting with the compiler" (or similar phrasing). I think this is due to a poor introduction to static type systems, often in university settings.

A static type system has one purpose: to prevent you from running unsafe code. Every type error is just the compiler telling you "what you wrote isn't going to work". This isn't a fight; it's a conversation. You write some code, run the compiler, get back some errors, fix them, and run the compiler again. This process is repeated until your mental model of how the code should work is properly reflect in the code, as determined by the success of your compiler's type-checking.

There are frequently two sources of frustration in this process:

1. A difficult-to-use type system.

Some type systems are just obnoxious to use. (Older) Java required soooo much verbose typing. The resulting code would be difficult to read just because there was so much text on the screen. More modern Java is an improvement, but newer languages are often designed from the ground up to use type inference to alleviate your keyboard-typing obligation.

Another form of frustration is what you mention: inscrutable error messages. Haskell is pretty well known for having type errors that are not obvious to newcomers, and that turns the programmer-compiler conversation into a frustrating experience. Again, newer languages are making lots of progress here. I think Rust does a great job with its error messages, for example. Hopefully this is something the Haskell developers will look more into.

2. An insufficiently expressive type system.

If your type system is too limited, you'll have a hard time finding it particularly useful. If your types are "too big", they become unwieldy and unhelpful. Older languages frequently fared poorly in this metric, because "types" were meant primarily for memory optimization rather than programmer expressiveness. However, as our languages have become more user-centered, our type systems have become more expressive and, thus, easier to use to model data. Modern languages like Rust and Swift have taken a lot of inspiration from functional languages like Haskell and ML to provide type capabilities that are significantly more expressive than those in other languages.

I think type system inexpressiveness is a big part of why some people prefer dynamically typed languages, like Python. They find that they have a difficult time converting their mental model of the data into an explicit type, and so they move to an ecosystem where they simply don't have to deal with that. Personally, I think this indicates the need for better expressive type systems and better type system onboarding to teach people how to use them effectively, because if you rephrase the static type proposition to those programmers as "Would you like to guarantee that erroneous programs are simply never run?", I imagine most of them would enthusiastically shout "Yes!"

> In my experience, people often describe their first experience with expressive static type systems in terms of "fighting with the compiler" (or similar phrasing). I think this is due to a poor introduction to static type systems, often in university settings.

Maybe that was true 20 years ago, but most CS curricula offer mandatory (static) functional programming classes.

Depends on the school culture, the professor teaching, the particular language being used... there are a lot of factors!

I did both my undergraduate and MS in the past decade, worked in academic research for a few years, and am currently in a PhD program. At the schools I've been at and schools I know well due to friends/collaborators, I think what I said still tends to be true for mandatory ("core") classes.

You are right, however, that students who choose to take an elective in a statically typed functional language are probably more likely to come away with a healthier relationship with their compilers, but this is only a portion of all students.

I would be shocked if that were actually true.

Maybe most top 10 programs do, but in general the variation in CS curricula is incredibly broad and includes tons of Java schools that only focus on imperative and OO paradigms with only passing mentions of functional programming in elective classes.

I really have no idea if the data to support the claim even exists. One would probably have to manually comb through CS curricula to check. My suspicion is that it would not hold up.

I fully expected to spend time "fighting with the compiler" when playing with Haskell. I think the bigger issue was that the errors I got back from the compiler were very hard to trace back to the code, or to a particular part of my mental model that must be at fault. And it didn't seem to get easier over time - i.e. the same pattern of error message would be due to totally different issues with the model/code.

I'm not sure if this is just a fact of life about FP or Haskell, or if it's something that could be improved on.

> I fully expected to spend time "fighting with the compiler" when playing with Haskell.

Well that's part of the problem, is what I mean. If you look at it as a "fight", that's often how it will feel!

That said, as I also said, Haskell's error messages tend to be more frustrating and mysterious than they ought to be.

> I'm not sure if this is just a fact of life about FP or Haskell, or if it's something that could be improved on.

No matter the cause, it can certainly be improved. Programming languages have largely not focused on user-centered design. Fortunately, there are some recently formed workshops seeking to get the ball rolling in that area, so hopefully over the next couple of decades we will see some significant shifts in the ease of use of popular languages!

That said, I think if you had tried a different statically typed functional language (OCaml or Scala, for instance), you would not be quite as frustrated. But then Haskell has my favorite type system among common statically typed functional languages, so there's really no winning haha. Hopefully it will improve over time!

> A static type system has one purpose: to prevent you from running unsafe code. Every type error is just the compiler telling you "what you wrote isn't going to work". This isn't a fight; it's a conversation. You write some code, run the compiler, get back some errors, fix them, and run the compiler again. This process is repeated until your mental model of how the code should work is properly reflect in the code, as determined by the success of your compiler's type-checking.

That's not true. Java or C++ force you to type out the types every time to

    - place constrains inside your program (both the caller and the called side)
    - be explicit about it (if anyone else wants to read your code later)
E.g x = 5, y = 6, x+y does work in many languages. (Untyped languages, or languages with type inference.)

It is an unambiguous command, and many languages would just do what you say, instead of forcing you to write code in a particular way that has absolutely no use for you.

Being said, I absolutely love Haskell's function type notation ( f :: A->B f x = doSomeThing x ), its type inference, and how easy it is to alias types. I want this notation in every other languages please.

> E.g x = 5, y = 6, x+y does work in many languages. (Untyped languages, or languages with type inference.) > It is an unambiguous command, and many languages would just do what you say

It isn't so unambiguous when you use x = 230 and y = 30 for example...

> That's not true. Java or C++ force you to type out the types every time to

That's not true for C++, with the auto keyword, which was a God send and made the code a lot more readable.

Strongly static languages like the ML ones are like having auto on drugs. If you like auto in C++, you'll like these languages. If not, you won't.

> That's not true.

It is unclear to me which part of my comment is "not true". Could you elaborate?

> E.g x = 5, y = 6, x+y does work in many languages. > [...] > It is an unambiguous command

It is absolutely not unambiguous; you only think it is because it happens to work the same way in most languages.

I also found that my initial understanding of the numeric types, based on a traditional imperative programming experience, was not sufficient.

However - I now think that I understand Haskell's way of looking at numeric types and have not had this kind of problem for quite a while.

I will certainly agree that Rust's error messages are 1000x better, if you're looking for something similar.

After writing this comment I looked at TFA and I'd say read it and maybe even watch the author's talk on "why learning Haskell is hard". In spite of what I expected, the article addresses the ways in which Dhall is a good starting point. (Although readers should be aware that "Dhall is not Turing complete", which for most programmers is a bad thing.)

Neat article!

Haskell does have escape hatches though! They're incredibly annoying to use: being both noisy and easy to spot. This forces developers to justify their use because it's going to be annoying to maintain. This is exactly what you want in an escape hatch! Not convenience but being easy to spot.

If it's easier to do it the Haskell way then you're often on the right path. Going off the path is dangerous and some times necessary, so don't hide it and make it easy to miss!

In my opinion Haskell's capabilities to help implement general-purpose SW (services, compilers, GUI, game-dev, systems) are way over-hyped.

Sure, it's nice to implement parsers/compilers in it (see ̶N̶̶̶i̶̶̶x̶̶̶, Elm, DHall) and it's to some extent popular for backend web development, but I don't think the strong static type system is such an advantage as advocates want you to believe.

Of course it's safer (and faster) than dynamic languages like Python (Ruby, JavaScript), but in my personal opinion, you don't gain that much if you compare it to something like Rust, Java or even Go.

In particular the common abstractions (Monad, Applicative, Functor) are used very differently in different libraries (everyone seems to want to write her own DSL), so it's hard to get comfortable with third party libraries.

Furthermore it's still the case, that there's not much really interesting SW made in it. Some years ago you could argue, it's because of familiarity, but (static typed) functional programming is part of CS curricula for at least 10 to 20 years now.

Lastly it's just incredible hard to estimate runtime characteristics (clock-time, memory usage), because you never know which compiler optimizations kick in or not.

It's a nice language, but again, I don't think it's such a panacea as proponents often tell you.

And to be snarky: If Haskell is such an advanced language to make implementing reliable SW such a breeze, it should be part (maybe as a dependency) of more SW.

EDIT: Nix is implemented in C++ as correctly pointed out in a comment.

I only agree with the runtime characteristics point. It's very hard to get an idea of what is efficient or not sometimes. But on every other point I believe Haskell is magnitudes better for general software development then mainstream languages.
That's my point (sry I edited my post, when you already replied).

It's a good programming languages, but being "magnitudes better" is absolutely not true in my experience.

Perhaps the ideal language is then a combination of two approaches: Haskell in the outer loops, Rust in the inner loops?
The Python code using C libraries you get on machine learning is a nice model. There is no reason why you can't recreate it with Haskell + Rust.

But it has one drawback that it's relatively slow to pack stuff to send between languages. On Python everything is so slow that this doesn't make much of a difference, but on the Haskell + Rust scenario you will want much larger library blocks.

You will lose the benefits of both type systems if you try to combine them. Rust doesn't really track effects, while Haskell doesn't care about memory.
I wonder if the first point is due to a paradox of advanced tools.. society lives around "how do you assemble teams to solve big problems", they need organization tricks more than logic/language ones. That's why RoR, go, etc win, they allow most groups to end up with something workable. Haskell and the likes are more about exploration.. not tending to companies needs for profits.

About the last point, my tiny observations is that language too advanced don't get used as dependencies.. they get ignored until the crowd matures enough to get the ideas in their own ways and pull a gene transfer.

- lisp idioms (map, filter, destruct) => all dyn langs

- react / vue .. basically let over lambda and closure based programming (I even believe vue provide/inject is near equal to dynamic variables)

wait until someone there likes to thread state or rng seeds through 'chains of functions' and you'll see monads pop up

Haskell does have advanced tools though. Stack / cabal, haddock, htest can be comparable to Rust’s tooling. Haskell’s LSP is making rapid progress and there are many formatters and linters.

Tooling still has a way to go, but I don’t think that’s all. Haskell’s abstraction is just too much when you need to design something which is ultimately kind of straitforward, and most apps are (design app, game, utility app, web server, etc.)

I do think Haskell is more than a research language, it does have some use in practical, mainstream programming. In particular, Haskell is great at anything which is “input to output”, like compilers or document converters. Or in a program which is ultimately complex, and not just for optimization but “math” complexity, like math programs or simulators. In those programs the implicit laziness actually comes in handy.

But Haskell for a real-time video game or editor, and you’re just wrapping everything in an IO monad or some restricted stateful equivalent, and executing most of the instructions in sequence anyways.

Why would you wrap things in an IO monad for a real time video game? There's a whole lot of code you can do purely functionally in video games that can make it very nice.

I've done a couple small projects in Blueprint which is a purely functional programming language embedded in UnrealEngine 4 and I enjoyed it a lot. Blueprint notably does not expose any type classes, at least not when I tried it a couple years ago, but it felt very expressive for building complex game logic.

FYI, Blueprint is very much not purely functional and has exposed type classes since it changed from Kismet IIRC.
Maybe I'm not remembering it correctly. Basically you had colored wires representing values flowing through functions, and then you had white wires that represented ordered execution, much like the IO monad. Is it possible to have side effects from nodes without the white wire?
> Stack / cabal, haddock, htest can be comparable to Rust’s tooling.

I don't think they are. Having used both, Rust's tooling is more accessible to beginners, which makes a big difference. I find it even easier to use than npm, while I find Haskell's tooling harder (on roughly the same level as OCaml with dune, or Maven).

> Haskell’s LSP is making rapid progress and there are many formatters and linters.

Haskell's LSP is good, however it's not as good as rust-analyzer, and has a habit of chewing through a lot of RAM.

> Stack / cabal, haddock, htest can be comparable to Rust’s tooling.

Disagree from me. I spent a year writing Haskell professionally, and from my own personal experience Haskell's tooling was overly complicated and difficult to use.

Maybe my impression is totally wrong or outdated, and it's surely not applicable to the whole community, so I'm sorry if this is a little out of touch now, but I sometimes got the sense that Haskell's community just doesn't care about the developer experience. They hold to this false dichotomy that if you make tools easy to use, then you can't have advanced features. Or simply, Cabal works just fine the way it is, why do we need Stack? Why would you need more than one GHC installation on a machine? Holy wars on the mailing lists re: Stack, Cabal, Haskell Platform. And if someone tells me to "just use Nix" one more time I will cry...

Rust feels like a breath of fresh air. The community seriously cares about their developer experience both for newbies and experienced professionals, and it really feels like a lot of work has gone into making the tools and documentation accessible and useful.

> I wonder if the first point is due to a paradox of advanced tools.. society lives around "how do you assemble teams to solve big problems", they need organization tricks more than logic/language ones. That's why RoR, go, etc win, they allow most groups to end up with something workable. Haskell and the likes are more about exploration.. not tending to companies needs for profits.

I feel like "not tending to companies needs for profits" is a way to elevate Haskell and monadic FP abstractions with appeal to conspiracy/(postmodern) ideology. I don't mean this to be a pro-or-anti capitalism sentiment, but the excuse here is that "in a more just/less captalist/<other> world, Haskell would be a great tool, but our flawed world makes it not so" and I disagree with that. I just don't think Haskell is that good of a tool regardless (and I see the Haskell community these days appealing to this take a lot, trying to blame anyone but themselves.)

I think that people genuinely don't derive much value from Haskell and monadic FP. My experience, having written and maintained non-production-grade Haskell code, has been that the community is obsessed with toys and small examples, derives a lot of pleasure from clever abstractions which don't materially affect development (like Lenses), and most importantly doesn't really care about shipping software. There's exceptions like SPJ and Joey Hess who definitely ship useful Haskell, but the community doesn't care nearly enough about making it easy to actually produce software that a non-Haskell enthusiast would care about using.

Libraries are often created for a Masters project and abandoned. Even if a library works, debugging edge cases is never thought of. Almost nothing is written about testing in Haskell. No two libraries can agree on which monad stack to use, so a non-trivial amount of integration code involves matching and lifting into other monads (nor the performance implications of doing all of this matching and lifting). There are very few examples of organizational patterns for Haskell codebases for folks trying to create a project other than GHC itself, Pandoc, and perhaps XMonad. The community loves talking about abstractions and unifying concepts but doesn't really seem to have any practical tips about writing software. The string types in the language are confusing and poorly differentiated. The cabal and stack split is confusing and makes no sense to outsiders. The weird cargo-cult obsession in the community about category theory from people with little formal mathematical knowledge is bizarre. I can go on and on, but I don't want to dunk on the language endlessly (and I can certainly do this to any language I've used in love or anger :)

Fundamentally I don't think Haskell offers the programmer much more than what you'd get out of a language like Rust, Java, or Go (yes even Go) as the GP said. It's a language with a lot of interesting concepts that's fun for folks who like to solve algebra problems and is certainly a clean introduction to monadic FP. I'm looking forward to a successor that takes its concepts, cleans them up, and offers a way to write actual software with them. Then again, Multicore Ocaml is landing soon...

Several of the issues you talk about are true enough, especially the ones about the community not making it easier for newcomers to get the ropes.

The reason for that is that people who do haskell for a living are dwarfed by the hobbyist community which makes them invisible, and they're spread thin on maintaining the ecosystem. GHC, HLS and all the other good things there are are the work of people on their free time, while other, more successful languages simply have companies dedicating time, money and contracts to tooling.

All of that means that it's not easy to move from the hobbyist haskeller group to the paid haskeller group. Most people usually get there by getting hired in a place where the stack, library choices, etc. are made by people who had done haskell professionally before and have solved the problem you cite for the team.

Once you're in, the benefits of Haskell are tangible, otherwise people would stop using it, and there wouldn't be a professional community. That is not to say that there are no problems, it's just that they are different problems, and people that stick to haskell for their job usually do it as a tradeoff.

What are the problems y'all see in production then with Haskell code? I'm curious since I never ran my code in a prod setting so I don't know what one cares about when operating Haskell code.
That would be a nice "ask hn" :-)
Honestly, the problems I had with Haskell were mostly related to organization rather than tech. How recruitment would happen. What rules we would set in place to make sure the codebase didn't become overcomplicated. How to make sure people don't get engrossed in tech and neglect the business aspect. How to make sure a large part of the codebase is mundane enough that newcomers can contribute on it quickly.

The tech itself really works well, but it required either an org mature enough to do the things correctly, or a shop purely focused on haskell that would accept the complexity.

Also, sometimes the library ecosystem is a bit lacking. It wasn't an immovable roadblock, but we did write stuff that may have been available as a lib in other languages. This was a minor impediment for us, but it can get bad in other contexts.

> Most people usually get there by getting hired in a place where the stack, library choices, etc. are made by people who had done haskell professionally before and have solved the problem you cite for the team.

That's very true and the case at my job. We've taught people with 0 Haskell experience to write useful production code in under a month.

> derives a lot of pleasure from clever abstractions which don't materially affect development (like Lenses),

Oh but they do materially affect development in the same type of way LINQ does for C# programmers.

This comment has a lot of facts wrong.

> Even if a library works, debugging edge cases is never thought of.

Many libraries use sum types to express their api which naturally leads one to consider edge cases.

> Almost nothing is written about testing in Haskell.

Yes there is! Did you verify your claim with a quick search? Here's what one gave me:

- https://mmhaskell.com/testing/test-driven-development

- https://jaspervdj.be/posts/2015-03-13-practical-testing-in-h...

Heck, here's one from 2007:

https://www.jasani.org/posts/2007/unit-testing-with-hunit-in...

It feels like this web of lies about Haskeller's not caring about real world concerns is being spun, but even with minimal effort that is trivial to disprove.

> No two libraries can agree on which monad stack to use,

The mtl library is the most used monad stack by far and it's not typical to see others very often.

> so a non-trivial amount of integration code involves matching and lifting into other monads (nor the performance implications of doing all of this matching and lifting).

I feel this may betray a lack of understanding because lifting between monad stacks is expected.

I agree there are performance implications on theory, but if you get out of the performance ivory tower you'll find it doesn't happen much in the real world ;)

We already sort of see threading state through chains of (pure) functions; it's how you write React, especially if you use Redux instead of hooks. You lug props around, they are explicitly immutable because you always get a copy, and it's fine, nobody complains much. It feels logical.

Some more effort in packaging this ergonomically, and we'll see purity annotations in TS and explicit passage of state objects around.

redux is another part of the 'gene transfer idea', nobody used reducers for anything except maybe rich hickey
The "fungible programmer" theory [0] is one of the better explanations for the unpopularity of languages like Haskell and Lisp. Ultimately, making software that's large enough will become an organizational problem as well as technical one, and developers will get funged into being blub programmers [1], though RoR is a pretty nice way to do that all things considered. I would argue Go was designed for maximum fungibility.

[0] http://www.loper-os.org/?p=69

[1] http://www.paulgraham.com/avg.html

Lol the fungible programmer theory is one of the most disrespectful theories of programmer choice out there. It assumes that, by default, everyone is a bad programmer and that only people that use "non-blub languages" are good programmers. Blub itself is vaguely defined or generally never defined rigorously. I never find a theory that has contempt for so much of an industry has any real explanatory benefit.
It's largely a demonstration that if you give pg an incredible amount of money by accidentally inventing online stores, he will develop an incredible ego and decide it was all due to his personal technical brilliance.
It's not necessarily that they are worse programmers, but rather represent the industrialization of building software, as outlined by Brad Cox [0]. Partially driving this is the shorter tenure of today's software engineers. How many beautiful things have had to be completely rewritten because the original creator had left, and no one could grok it? Not that there is no way to reconcile fungibility with expressiveness and beauty, but the tradeoffs involved are tricky and will always end up disappointing somebody.

[0] https://www.researchgate.net/publication/3246772_Planning_th...

Dan Luu has a take on this: http://danluu.com/butler-lampson-1999/
Thanks, interesting article.
Interesting take but I find the RISC argument to be completely flawed in light of ARM/M1.

Even accepting the distinction (which I don't agree with) between "non RISC-y" ARM (v7 and earlier) and "RISC-y" (v8 and later), Apple M1 is "RISC-y" by that definition (instruction set ARMv8.5A)[1]. And it's doing well.

[1] Line 256 https://github.com/llvm/llvm-project/blob/main/llvm/include/...

It’s a really odd take given that by 2015 Apple was already on Arm v8 for the iPhone and the performance gains over v7 were clear. Calling it ‘hopeless’ seems incredibly wide of the mark.
ARMv8 is a well designed architecture that I don't think is "RISC" because eg it has some complex addressing modes, SIMD, and other good ideas.

It is more RISC than ARMv7 was. And I think if it was targeting smaller CPUs with less wide decoders, being more x86-like ("CISC") and having variable length instructions would be better.

Thoughts and prayers to all the people whose heads just exploded by reading that Haskell doesn't gain you that much compared to Go. (I tend to agree)
> In particular the common abstractions (Monad, Applicative, Functor) are used very differently in different libraries (everyone seems to want to write her own DSL), so it's hard to get comfortable with third party libraries.

I don’t see how this is true. These common abstraction make reading code incredibly easy most of the time. Many Haskell libraries are based on compositional building blocks. Seeing a monoid, functor or monad instance gets you 90% there in understanding the usage. It’s really the superpower of Haskell that I miss (and badly reimplement) in every other PL.

Completely agreed, but I guess this just doesn't click with some people as well as it did with me (empirically).
I was also wondering about this. I can't think of an example of a library implementing Functor in a surprising way, or in a way that somehow differs from another library.
Exactly! And free theorems and polymorphism means even more things work as someone that understands the core abstractions would expect them to.
> Sure, it's nice to implement parsers/compilers in it (see Nix, Elm, DHall) [...]

Nix is implemented in C++, not Haskell

Oh, I thought it was Haskell. Thanks, fixed.
> functional programming is part of CS curricula for at least 10 to 20 years now

My school (UIUC) only just added a functional programming course to the CS course map in 2021. Of course you could always take a functional programming course if you wanted to, but not many do.

Interesting. I graduated a decade ago and remember taking CS421 where you implement part of an OCaml compiler in OCaml. I thought that it was required because I don't think I would have chosen it myself. It ended up being my favorite course in all of school.
>Furthermore it's still the case, that there's not much really interesting SW made in it.

TidalCycles would like to have a word with you. Easily one of the most interesting and unusual pieces of SW that currently exists.

I played with Tidal when it first came out for music. I do like it. To me though, I prefer Grace[1] (CM, Lisp) and Extempore[2] (Scheme, xtlang). You can work within the grammar of music, or you can program your own DSPs. Xtlang in Extempore is a pretty neat DSL created by Andrew Sorensen. To me Tidal was mainly rhythm-based. I am sure it has evolved since then, but it felt like a coding sequencer or pattern generator. I liked Sporth, (Soundpipe + Forth), but it appears something has occurred on the internets that I am not aware of. The repo is still here[3], but it is not being worked on any longer from what I have read there.

If interesting and unusual is your thing for generative music, check out Orca[4]. That has eaten hours of my time far more than Tidal for me.

[1] http://commonmusic.sourceforge.net/

[2] https://extemporelang.github.io/

[3] https://git.sr.ht/~pbatch/soundpipe

[4] https://100r.co/site/orca.html

> In my opinion Haskell's capabilities to help implement general-purpose SW (services, compilers, GUI, game-dev, systems) are way over-hyped.

Haskell usage in companies is currently niche at best. It's hard to know what you would consider reasonable use of the language if you believe it's currently overhyped. Maybe your idea of hype relies on conference talks or some other metric I don't get?

> in my personal opinion, you don't gain that much if you compare it to something like Rust, Java or even Go.

Rust is pretty similar to Haskell in terms of type system experience, the main difference being that, when you don't really care about how memory is used, Rust forces you to handle it. Any GC'd language will feel refreshing if you're annoyed by that. Aside from that, I don't challenge that Rust is a great language to program in.

Java's usability is built on a fundamentally different model: it benefits from decades of investment in IDEs, and all professional java users I know use it extensively. While a haskeller will rely on types to perform refactoring, a java dev will refactor using the IDE, with an equivalent peace of mind. Java's typesystem is poor, but it doesn't really matter in users' development experience.

I don't have much positive stuff to say about Go, and even less about its typesystem. You may find better advocates for this one.

> Lastly it's just incredible hard to estimate runtime characteristics

Most use cases don't care about that. You quoted Java earlier, a language in which most devs never ever really delve into runtime performance and simply let the runtime do its stuff. As it turns out, it's usually good enough.

It's quite surprising that aside from Fortran, C derivatives and Rust, no one gives a damn about performance, but when it's haskell, it suddenly becomes a problem.

> If Haskell is such an advanced language to make implementing reliable SW such a breeze

Is that some kind of strawman where if haskell doesn't deliver on being touted as the gift of God to programmers, then it's bad?

> [...] Maybe your idea of hype relies on conference talks or some other metric I don't get?

Yes, I'm referring to programmer circle related social networks (HN, r/programming, ...).

> It's quite surprising that aside from Fortran, C derivatives and Rust, no one gives a damn about performance, but when it's haskell, it suddenly becomes a problem.

Because performance problems are particularly hard to solve in Haskell due to its high level of abstraction.

> Is that some kind of strawman where if haskell doesn't deliver on being touted as the gift of God to programmers, then it's bad?

Yes, given the fact that it's praised to such a high degree, users might have extremely high expectations, but are likely to be underwhelmed if it does not solve their problems in a vastly superior way.

> Yes, I'm referring to programmer circle related social networks (HN, r/programming, ...).

> Yes, given the fact that it's praised to such a high degree, users might have extremely high expectations

May I suggest the problem is not with the language, then? As a fellow HNer that you can trust, I also have an extremely well-made bridge to sell you if you're interested.

> Because performance problems are particularly hard to solve in Haskell due to its high level of abstraction.

That doesn't match with my experience using the language. But again, how often do you have to solve performance issues that are more complex than a misplaced quadratic function in other languages? If it's frequent enough, wouldn't you consider using a language like rust or CPP rather than something like Java, Go or Python?

Yes, if performance is the main concern, Rust looks good. However if I can get by with GC I still would choose something like Go, as I am just getting to a working solution faster.

And I spent way less time learning Go than learning Haskell.

> And I spent way less time learning Go than learning Haskell.

...And you've got what you paid for.

Google paid quite a lot internally to develop Go, and despite its many flaws it at least has mechanical sympathy for the computers it runs on.

I've read GHC assembly and it seems to be intentionally trying to run poorly - eg it calls everything through unpredictable function pointers. No, this isn't CPUs' fault, as GHC people do try to be optimal, they are just failing. jhc produced good code but isn't maintained afaik.

Maybe Haskell is overpriced?
In terms of learning, Haskell is like a lootbox game. You can definitely do great things with a minimal budget, but people get addicted and end up spending fortunes on additional content ;)
> But again, how often do you have to solve performance issues that are more complex than a misplaced quadratic function in other languages? If it's frequent enough, wouldn't you consider using a language like rust or CPP rather than something like Java, Go or Python?

Why? This doesn't agree with my experience using Java or Go at all. Both languages have ways to reuse objects that significantly decrease GC pressure for bottlenecks to achieve competitive performance. Also anyone running net services at scale frequently runs into performance issues that are much more thorny than misplaced quadratic functions (are you deserializing JSON in a hot loop? is your HTTP connection pool too large? is your hash table too large, leading to significant collisions?)

Rust or C++ is overkill when reusing some objects, moving data structures, or memoizing some serde is all you need. Reasoning about them in Haskell can also be quite difficult, especially if you build a lazy chain of operations that leads to executing an expensive operation frequently in a hot loop, or forget to preallocate HTTP connections in a pool, or something like that. It's a high cost to pay for a better type system and awkwardly unifying abstractions.

> Both languages have ways to reuse objects that significantly decrease GC pressure for bottlenecks to achieve competitive performance.

I agree with that, but the consequence of these things is that developers don't have to care about it themselves. Thus these languages are usually good enough that optimization is not a concern for most devs in these ecosystems.

> (are you deserializing JSON in a hot loop? is your HTTP connection pool too large? is your hash table too large, leading to significant collisions?)

None of these things are language-related, though.

> Reasoning about them in Haskell can also be quite difficult

We didn't have specific trouble when this kind of problem came up, maybe your mileage is different ? Being used to it, it's kind of hard to understand what specifically you find hard in this.

> Because performance problems are particularly hard to solve in Haskell due to its high level of abstraction.

Does not match my experience. Haskell typically compiles to quite performant code, at least for all the cases that I’ve used it for.

Then few times I had to optimize anything for performance, it did not feel different than other languages. It was in almost every case about the architecture of the code and not something to do with the language itself.

In trying to figure out why Haskell hasn't been widely adopted, I think it's very hard to disentangle legitimate criticisms of the language (unpredictable performance, overcomplicated and bizarre APIs, etc.) from the simple fact that learning it with an imperative background is like starting from scratch, even for experienced programmers. This is true even if you look at the base language, without involving Template Haskell or any of the weird language extensions.

Ultimately, asking programmers to relearn how to be programmers (or, more relevantly, asking businesses to accept that overhead and/or a vastly reduced hiring pool) is just too large of an ask for whatever marginal gain you might expect from using a "superior" programming language.

To be clear, I learned Haskell for fun and got to what I'd consider an intermediate level of expertise. It was an incredibly enriching experience, and gave me a new perspective on programming in general. But it was hard, and the rabbit hole just keeps going---sure, you can juggle monads in your sleep, but are you proficient with the `lens` library? No? Well, I can recommend you a pile of textbooks that might help.

This is where I'm at. I don't know if I'll ever get useful software out of it, but I really enjoy the mental challenge. I actually find it a bit of an ugly language to look at (backticks, hanging single quotes etc) but I like how it reveals the underlying structure of problems that I haven't been able too replicate in other languages.

Though I'd recommend a youtube channel called 'Low Level Javascript' (really!) which comes close in developing some of their parser combinator library.

Hanging single quotation marks? You mean the ‘prime’ symbol? This comes up a lot in math to generate more variables that need a similar name—that’s why it ends up in Haskell and related languages. It’s not something weird.

Granted the ASCII tick/apostrophe is neither the proper symbol for primes nor quotation marks (should be curly), but programmers are reluctant towards Unicode.

What about Rust? Rust doesn’t go as in-depth as Haskell when it comes to modeling effects in the type system, but it still has a relatively complex type system on top of dealing with the borrow checker (which is almost as joked about as Monads at this point). Even with that difficulty, Rust seems to be a lot more successful than Haskell, and has existed for far less time.

If it’s because Rust is still mostly imperative, then you could look to Clojure or even Scala—less complicated when it comes to types, but also (usually) much less imperative. Both I feel are more popular than Haskell.

I was thinking about Rust a lot when I wrote this comment, actually.

IMO, imperative vs. functional aside, Rust does suffer from a lot of the same learning curve issues people commonly pin on Haskell. Rust has plenty of libraries with weird, excessively "clever" APIs, weird experimental language features, a complicated metaprogramming facility, and so on. Personally, I have found lifetimes in Rust more annoying to deal with in semi-nontrivial situations than anything I ever encountered in Haskell, and it doesn't help that things that seem like they should work often don't.

(I always figure that's the language telling me that I should use a different memory management strategy, and I generally follow its advice, but I digress.)

This is very much my opinion, but I'd attribute Rust's success over Haskell to three main points:

* Functional vs. imperative, skill curves. Rust is still vastly more familiar to most programmers at a first impression, and IMO it also has a lower skill floor.

* Rust's tooling feels a lot more modern and streamlined. `cargo` is a joy to use. Maybe Haskell's ecosystem has improved, but back when I was actively using it, it was clearly inferior to Rust's. For me, this manifested mainly in significantly greater difficulties managing dependencies and reproducing builds. This stuff is a first-class priority for the Rust team, and it shows.

* Somewhat related to the last point, Rust is "cool". It isn't burdened by decades of academic cruft; it just feels approachable, modern, and relevant in a way Haskell doesn't.

The last two points aren't really Haskell's fault. I think the Rust team has done an absolutely top-tier job of making the language approachable and usable, obviously building on lessons of the past (including Haskell, Stack, cabal, haddock, etc.) and I'd almost say it's succeeded despite the essential nature of the Rust language itself.

The difference is actually much more simple. Rust fills a gap, Haskell does not.

Being purely functional seems like a massive win until you realize most of the real world benefit of that expresses itself as testability/correctness which can be obtained by other means. Lazy evaluation is actually a double edged sword so it's not a clear win either.

Rust on the other hand is adding safety and correctness assurance specifically without or with very little runtime overhead while providing very predictable performance (both in runtime and space).

You can sub Haskell out for Clojure/Scala/OCaml and arguably mixed paradigm languages like Kotlin/Rust/Swift and be fairly happy.

However if the constraints of your problem call for Rust then your only other real options are to sacrifice correctness/data safety and use C/C++ instead.

FWIW I think Haskell is awesome, this is just why I think we don't see it in "the real world' much.

> Being purely functional seems like a massive win until you realize most of the real world benefit of that expresses itself as testability/correctness which can be obtained by other means.

Is the end result reached by other means the same? Can you give an example of a competing approach in a non-pure language that gives one Haskell's advantages?

I don't think the result is the same but it is comparable. I would say using any language with a reasonable type system, say Kotlin w/property based testing gets you a result that while not as good as Haskell w/quickcheck is "good enough" for practical use in production software.

There is maybe some niches where that wouldn't be the case like very high assurance software where instead of Haskell you might choose say ADA instead but I'm not really familiar with those domains.

I actually think the construction method of and composition that aids code re-use very applicable to regular-degular workaday programming.

Kotlin plus property testing gets you very far and the industry defaulting to that would be a huge improvement.

I'll have to rewrite one of my Haskell projects in Kotlin and see if I feel anything is missing.

That would be a pretty interesting experiment!
For me the real selling point of Haskell is that it's vastly more powerful and ergonomic for solving certain kinds of problems, in ways other functional languages don't necessarily share. First-class currying, typeclasses, HKTs, do-notation and other features in that vein give Haskell code a level of effortless composability whose equal I haven't seen anywhere else, and the way pattern matching is built into every part of the language makes other languages' versions of it feel clumsy and primitive.

Back when I was fluent in it, I wouldn't reach for Haskell for functional purity or lazy evaluation---I'd reach for it because I wanted the richness and flexibility of its type system and pattern matching for building and manipulating a tricky data model, or because I was doing something involving tree-like linked structures and/or that lent itself well to recursion. Parsers are the obvious example, but they are a very narrow view of what Haskell excels at.

(In short, I'd reach for it for reasons that are basically impossible to explain to somebody who doesn't already know what I'm talking about.)

I can't be so effusive about Rust. It's a really great language with a thoughtful and restrained design, powerful and practical and flexible, but I would actually say the opposite re: filling gaps: Rust feels like more of an iterative improvement in the space occupied by C/C++/Go/etc., while Haskell is transcendently good for those classes of problem where it excels.

---

Today I'm more or less fluent in Rust after a few hours' warm-up, but it would take me days or weeks to warm up my Haskell. Why? Mostly because I've largely stopped programming for fun, and I use nothing like Haskell at work. Throw in the quality of Rust's tooling and the largely effortless reproducibility of its builds (great for warming up a 6+ month old project), and it's just a far more practical choice for the little hobby programming I do. Hence my earlier comment. But I still contend that Haskell has vast untapped potential, locked away behind an intractable adoption barrier. It's less that it has nothing new or useful to offer us, and more that we are unwilling to pay the price of admission.

I was keen to do a small project in Haskell. However I quickly found that the compiler gives cryptic errors. I am very capable of managing that (I have many years experience using a variety of old school compilers in the past), but I have no desire to repeat yet another compiler hell experience and waste my time fighting my tools, trying to diagnose where I have misunderstandings.

I haven’t yet tried programming rust, but I have read that error management and suggestions for correction in rust are superb. That makes a world of difference for a neophyte in a programming language.

Rust is doing well because it fills a usecase that has been crying out for a new solution for decades. Low level systems programming - kernels, microcontrollers, high performance applications - have basically had to be written in C or C++. Sure, under some circumstances people can get away with writing performance critical apps in other things, but you can't really write a real world OS kernel in anything else.

Rust brings a new set of tools and tricks and safety to an important use case that has had no movement for such a long time. The only real advancement has been smart pointers and "modern" C++, but while they are an improvement they also contribute to C++ being a bag of all the features.

> ...earning it with an imperative background is like starting from scratch, even for experienced programmers. This is true even if you look at the base language, without involving Template Haskell or any of the weird language extensions.

As a somewhat experienced programmer, I would actually say that the language extensions are an important part in disincentivizing learning Haskell for me, because even if I learn the base language, it seems like I would need to understand all these weird extensions to read real-life code, which always seems to involve half a dozen of them.

Yep agree. Keeping track of what all the language extensions do is not worth the effort compared with more leading edge languages like Agda and Idris.
As of GHC 9.0.2 the GHC2021 language pragma is available and it consists of the most popular language extensions.

https://downloads.haskell.org/~ghc/9.2.1/docs/html/users_gui...

Also typically language extensions are pretty simple, single purpose, and well documented.

Is this structured like rust editions? If so I think that could be very useful for the ecosystem.
> Also typically language extensions are pretty simple, single purpose, and well documented.

I will try again to explain with an example how these extensions are borderline user-hostile.

One of those extensions in your article linked above is apparently something called `DoAndIfThenElse` which enables if..then..else syntax with if and then on the same line, so far as I can tell [1].

When building programs that assume this extension without declaring that pragma, you get helpful error messages like "Unexpected semi-colons in conditional" [2]. Dealing with oddities like this seems to be routine any time I work on a Haskell project that's not a tutorial, and the constant need to go hunting for these error messages and add random pragma headers saps energy and is discouraging.

There also seems to be an open issue about the extension being undocumented [3], which would seem to contradict your "well documented" claim.

Given such significant problems in a relatively simple extension, I cannot even imagine how bad things are in more complex cases. I think whoever is steering the ship in Haskell/GHC land should really focus on ways to freeze new language extensions, and make the current ones well-documented in addition to providing options like the one you linked. Newer languages have environments that are much friendlier towards newcomers in terms of both documentation and (for lack of a better word) runnability of real-world code.

----------------------------------------

[1] I would really like to know why this is considered a popular language extension, and/or why it was needed, but let's skip over that for the moment.

[2] https://kseo.github.io/posts/2014-01-13-DoAndIfThenElse-lang...

[3] https://gitlab.haskell.org/ghc/ghc/-/issues/18631

> When building programs that assume this extension without declaring that pragma, you get helpful error messages like "Unexpected semi-colons in conditional" [2]. Dealing with oddities like this seems to be routine any time I work on a Haskell project that's not a tutorial, and the constant need to go hunting for these error messages and add random pragma headers saps energy and is discouraging.

Well thats kind of the point of the GHC2021 extension. It will be a standard so that new programmers that format if else statements as they would in other languages don't get a weird error that very much does SAP their energy.

> There also seems to be an open issue about the extension being undocumented [3], which would seem to contradict your "well documented" claim.

It certainly calls it into question, though I did say typically. I'm open to weakening to "many times" if there are more than a few other examples.

> Given such significant problems in a relatively simple extension, I cannot even imagine how bad things are in more complex cases.

The problems described occur as a result of the extension not being turned on, so I find the logic doesn't work here.

Yes! Make a new version of the language that includes the used extensions, freeze it for a while, add a few chapters to Learn You a Haskell. That would improve the experience of a learner who wants to move from toys to real projects.
Haskell is hard language I have like tried to learn it several times and no luck. I did not have same problem with Clojure and Rust where I was productive like first day.
Curious what method you used? I recently bought a book, I'm only learning it to port a Haskell project to C++, if I can even do that.
Most people require several attempts before they get over the hump
> asking businesses to accept that overhead and/or a vastly reduced hiring pool) is just too large of an ask for whatever marginal gain you might expect from using a "superior" programming language.

I feel the burden of proof for the claim "superior languages make only marginal differences" is one that is very high and one you have not met.

That's probably why I put 'superior' in "air quotes".

The null hypothesis would be that all reasonably well supported modern general-purpose programming languages are about as productive as each other on average. I have my own anecdotal opinion(s)^[1], but the onus is really on anybody claiming language X is superior (no air quotes there) to prove it to the rest of us.

In this case, by using air quotes, I meant to draw attention to the fact that the aforementioned proposition regarding Haskell's superiority is (AFAIK) unproven, as well as to illustrate the skeptical attitude a practical business leader might have toward embracing it, for what I consider to be totally fair reasons.

Also note that I'm not necessarily using 'marginal' in a pejorative sense. A marginal improvement can be significant.

^[1] Anecdotally I think Haskell is transcendently good for certain kinds of problems, and a huge pain in the ass for others.

> In this case, by using air quotes, I meant to draw attention to the fact that the aforementioned proposition regarding Haskell's superiority is (AFAIK) unproven,

I noticed that and considered (and should have said) this to prevent any miscommunication:

I feel the burden of proof for the claim "language choice makes only marginal differences" is one that is very high and one you have not met.

Then if we want to disprove the null hypothesis there we give one programmer brainfuck and the other Python ;)

> Then if we want to disprove the null hypothesis there we give one programmer brainfuck and the other Python

Hence "reasonably well supported modern general-purpose programming languages".

I think this null hypothesis is where most sufficiently jaded working programmers end up, and business leaders without a programming background will start there because it's all Greek to them. I'm not here to prove its correctness, just to raise it as a very real factor working against Haskell's adoption.

Nothing would delight me more than for somebody to come along and unequivocally prove that Haskell is a silver bullet. But the point here, really, is that nobody has yet.

> I have my own anecdotal opinion(s)^[1], but the onus is really on anybody claiming language X is superior (no air quotes there) to prove it to the rest of us.

There's also a burden of proof on those who claim there is no diffrence.

No. That Language X is not the best is the null hypothesis.
Most of the popular languages in use today have big corporations backing them. I would say that this is a major reason why Haskell and other niche programming languages are not more widely adopted.
Back when I learned it, it may have been true that Haskell's adoption was hurt by functional programming being an exotic thing.

But that isn't really the case today, is it? Almost all the cool power features of Haskell have been adopted by the rest of the world. Type inference, pattern matching, explicit nulls, algebraic data structures, rich type systems... although it isn't usually enforced, the world has woken up to the fact that "mutable state is dangerous and should be avoided whenever possible". Writing a program without any reassignment isn't nearly as alien to a programmer starting out today as it was 20 years ago. Back then, even passing a function as an argument was edgy (and, in C/C++, risky).

I think what's hampered Haskell, and made at least me reluctant to pick it up again, is that for all the ridiculous talent working on Haskell, there hasn't come some brilliant fix to the space leaks thing. Almost as if there's something fundamentally hard about composability and laziness. I'm really not sold on laziness by default anymore, it costs so much more than it tastes - and for everything else, Haskell is a victim of its own success in that so many of the things it pioneered has gone mainstream.

You can do enable strict evaluation by default in Haskell, though. So, that must not be the reason why the language is not mainstream.

More likely, network effects can explain how something makes it to the mainstream. For example having a big corporation behind the development and marketing of the language.

I wrote a bit about this here: https://news.ycombinator.com/item?id=31663316

In brief, I think Haskell is still the purest expression of what makes it great. This is both a good thing, for the reasons I outlined there, and a bad thing, because e.g. stringing together a few iterator methods in Rust may be a functional style but it still prepares you for the concepts you'll encounter in Haskell about as well as zooming a Hot Wheels around on the kitchen floor prepares you to drive a lap in an F1 car without stalling out or flying off the track.

People with a modern imperative background know this intuitively---or maybe they just assume it, and happen to be correct---as soon as they look at a page of nontrivial Haskell code.

One thing people seem to miss about laziness in Haskell is that it's (unfortunately?) really tightly coupled with how "control structures" are expressed. For instance, instead of writing a loop with some infinite or indefinite condition, you might declare a list using a generator function and iterate over its values. I would like to see how other similar languages (e.g. Purescript) have done away with it, and how it impacts them. But my recollection from when I actually knew Haskell is that it was an incredibly powerful feature tightly integrated with the language design, not only from a data perspective but also from a perspective of allowing code structures to map much more closely onto the data structures they're manipulating.

(This is a big part of why, as I think I mentioned somewhere else in this thread, Haskell is so ergonomically suited for working with tree-like linked data structures.)

> Of course it's safer (and faster) than dynamic languages like Python (Ruby, JavaScript), but in my personal opinion, you don't gain that much if you compare it to something like Rust, Java or even Go.

20 years ago you would've been saying C++. Rust is closer to the Haskell of 20 years ago than it is to most languages of 20 years ago. Hell, one could make a decent argument that Java 17 is closer to the Haskell of 20 years ago than it is to the Java of 20 years ago. Even Python is feeling the need to add types (and increasingly more people are using TypeScript rather than vanilla JavaScript), even Go is feeling the need to add generics...

> In particular the common abstractions (Monad, Applicative, Functor) are used very differently in different libraries (everyone seems to want to write her own DSL), so it's hard to get comfortable with third party libraries.

This is completely backwards IME. In most languages, every new framework comes with its own abstractions that you have to learn (whether that's, IDK, Spring Beans, ActiveRecord, React Hooks, ...). Haskell-like languages where there are a few standard meta-abstractions like Monad/Applicative/Functor make it an order of magnitude easier to pick up a new framework, because they mean core abstractions work the same in every framework.

All that said, I do kind of agree with your conclusions, but I think that's more about the industry being dishonest about its priorities. IME Haskell really does deliver what it says: for less than twice the effort of something like Python, you can write software that approximately never has bugs, in the sense that it will always do what it was specified to do. But most of the industry doesn't actually care about getting rid of bugs; they'd rather have buggier software in less development time (and if that means more maintenance effort in the long term, well, who cares).

> Haskell-like languages where there are a few standard meta-abstractions like Monad/Applicative/Functor make it an order of magnitude easier to pick up a new framework, because they mean core abstractions work the same in every framework.

Exactly! The abstractions are less leaky, more sound, and give this gift and feeling of universality, unrivaled composition, and cohesion.

I still think Elm is a better example for reliable (web) software development. I've simply never had a similar experience in terms of the high amount of type safety enforced by the compiler. Interestingly I found I was much less fatigued after programming in it (and this applies for Haskell too) because the problems one has to solve aren't really stupid ones.

(I don't recommend it for production only because it has been partially abandoned, it seems, but it if hadn't been and the community had kept growing it would have been awesome).

I don't think your snarky section is that relevant, because it's hardly like the best technology wins. Software engineering has a bias towards either irrelevant nonsense to pad resumes or extreme conservatism.

> I don't recommend it for production only because it has been partially abandoned

Why do you say that?

Sure, there were some dramas around how the language/ecosystem should evolve and almost all external opinions are not considered. But again, you could argue that Elm is consistent because of this heavy handed approach to language design. I'm just wondering why you would consider it as `partially abandoned`.

Too many high profile community members basically saying that it's impossible to work with the core team on improvements, with no clear communication that there even is a core team left any more, and bugs in the standard library/compiler that haven't gone fixed despite being one line changes. Many, many pull requests with such fixes have gone ignored for 3+ years. No indication of any possibility for a superior FFI or at least a path forward for the evolution of the current one.

The language itself is still mostly fine, so long as you don't run into its limitations. I still wouldn't recommend it going forward for the above reasons, though. Too many warning signs and contributor goodwill unnecessarily burnt. Evan is a perfectionist and clearly has no interest in developing a feature if he can't see the use case for it (his response to negative numbers in case..of expressions was a classic example).

Personally I think it's all a bit of a shame, Elm is one of my favourite things ever in web development. I really, really wanted/want it or something similar to succeed in terms of major projects being written in it.

Maybe check out Mint - https://mint-lang.com/

This is an effort that takes best ideas from Elm. AFAIK, the creator was one of the high profile community members (or atleast a prolific Elm user).

I'm not sure how this language will pan out. There are too much options for UI development right now. Also, last when I checked, you have to buy in on this framework/lang completely and you had no option of plugging this in an existing project.

Still, I think Elm users may find this interesting.

What about pandoc. https://en.wikipedia.org/wiki/Pandoc

Author is not a "dev" nor a self-proclaimed "engineer". He has no computer science, maths, EE or physics degrees listed on his CV. He does list several languages. None are computer languages. He suggests the programs he has written may be "structured procrastination".

http://johnmacfarlane.net/macfarlane-cv.html

https://www.johnmacfarlane.net/BayHac2014/

https://usesthis.com/interviews/john.macfarlane/

I think Dhall looks interesting. Like MacFarlane, I am free to choose any language I find satisfying for procrastination purposes.

> Author is not a "dev" nor a self-proclaimed "engineer". He has no computer science, maths, EE or physics degrees listed on his CV. He does list several languages.

To piggy-back on this I'm a professional Haskell programmer with no degree and my math education stopped at shaky pre-calculus.

> In particular the common abstractions (Monad, Applicative, Functor) are used very differently in different libraries (everyone seems to want to write her own DSL), so it's hard to get comfortable with third party libraries.

This is the opposite of my experience as a professional of many years. Most Haskellers I know think that this thing you're complaining about is extremely helpful, actually.

The fact that every library gives you shared instances for wildly different things makes them easier to use. If you give me an Applicative instance, I'll be able to use it in so many ways.

It's a shared abstraction language. No other PL compares.

At the end of the day, none of the languages you mention come close to being able build complicated programs out of smaller ones like Haskell can. It's all gunk, inherently.

I use my brain to write all those languages in an exhausting way. I barely do hard thinking with Haskell ever. Whether it be gamedev, web services, parsers .. I thank god every day I don't have to live like a normal programmer. My mind would be so tired.

> I use my brain to write all those languages in an exhausting way. I barely do hard thinking with Haskell ever. Whether it be gamedev, web services, parsers .. I thank god every day I don't have to live like a normal programmer. My mind would be so tired.

I feel this. On slower days where I manage to eek out good productivity I joke that Haskell is the secret weapon of dumb and lazy people that managed to somehow not be lazy learning Haskell.

I like it for side projects for the same reason. It feels like you're always making progress even when you can't find time or energy. I have made a lot of progress on my indie game while doing the dishes because Haskell is so easy to do in your subconscious.
I know Haskell quite well but never reach for it when programming. Languages like F# and ocaml gives me most of the benefits of functional programming without having to deal with the unpredictability of lazy evaluation and the 10 level deep pollution of a single IO call. Also, when it comes to leading edge type systems, Haskell is falling behind. Dependently Typed languages like Agda and Idris are way more interesting from an advanced type system point of view.
> Dependently Typed languages like Agda and Idris are way more interesting from an advanced type system point of view.

Yeah, but with Haskell you have a usable healthy ecosystem for real world code now.

Yep that is correct. Which is why I am using Agda/Idris to expand my mind and F# for real world applications since F# can use the full .NET ecosystem.
> In particular the common abstractions (Monad, Applicative, Functor) are used very differently in different libraries (everyone seems to want to write her own DSL), so it's hard to get comfortable with third party libraries.

Can you give one example of this?

> Lastly it's just incredible hard to estimate runtime characteristics (clock-time, memory usage), because you never know which compiler optimizations kick in or not.

When you use it as a faster safer Python this almost never matters in the real world.

I think people are missing the fact that Haskell was created for programming language research, and not for commercial purposes. It is somewhat of an accident that people started using it outside of its original domain. It was never meant for "general purpose SW".
> you don't gain that much if you compare it to something like Rust, Java or even Go.

Some languages have no implicit nullabillity and proper sum-types and exhausivity checks. This is what the main benefits of Haskell come from IMHO. Langs like Rust, Elm, OCaml/Reason/ReScript and Kotlin (even TypeScript) to some extend have this.

Go and Java dont have this, and I miss them every time I use these languages.

>And to be snarky...

Lol, that is not how things work at all. An idea can be better and still be a minority. The imperative mindset has a lot of momentum.

The big wins from Haskell are immutability, pure functions by default, and expression-orientation... not the type-system, in my view.

As to why it's not more popular? Well the entire industry caught OOP fever during Java's heyday, so platform holders pushed languages that were similar - see JavaScript, C#, Visual C++, etc... Now this paradigm has huge inertia, and smart software engineers are able to make it work (just about) anyway. In other words, an accident of history got us caught in a local minimum.

Things are shifting though. The most popular framework in the world in probably React, and it's a (pretty crummy) implementation of functional programming concepts. It's not hard to find OOP thought-leaders advocating for immutability, effect systems and so on. Implementation inheritance is going the way of the singleton.

I agree that Haskell in particular suffers from being lazy, which has genuine problems in practice.

> I agree that Haskell in particular suffers from being lazy, which has genuine problems in practice.

- tooling is (used to be) bad

- compiler is hostile to new users

- even the simplest code is nigh undebuggable

- gotchas like foldl vs foldr due to lazy evaluation

I don't really think many people are calling it a panacea. From my experience in the community most people I know who write Haskell write it for productivity reasons (including me, I'd rather be using Idris 2 with algebraic effects though.)

I'm not saying it's somehow magically more productive than other languages, because it's useless to throw around stuff you can't really prove like that, just some people find it to be the case.

I've never found "everyone writing their own DSL" to be problematic, because it's all exposed through the Monad/Applicative/Functor classes, so the way you use them are all very similar. Honestly, the usage of such eDSLs is one of my favourite parts of writing Haskell.

I use Haskell at my work for our main application, and we've been looking at adopting a style guide like https://kowainik.github.io/posts/2019-02-06-style-guide

And about interesting applications in Haskell, I'd consider https://hasura.io/ pretty interesting!

EDIT: And about language extensions, yeah, they can be a problem. What we do is settle on a base set of extensions, and if you want to use another one, you need to provide sufficient justification for it.

(comment deleted)
> Haskell is hard.

More accurately: Haskell is particularly hard for a procedural programmer to learn in comparison to a new procedural language and the time spent learning prior to being as productive may be (from my experience) 30x.

IMO, once learned, the type checking is so helpful, everything else seems hard.

Also, learning the language is only half the battle as it’s much more difficult to establish for yourself good practices for common use cases like app development. You’ll find plenty of people using FP for specialized stuff that’s not relevant to the average working programmer. In looking for advice you may stumble across the promotion of peoples’ impractical pet projects, and without experience you won’t know it.

If you can budget the time to learn the language and then build a couple poorly structured apps that you’ll come to hate, expect very smooth sailing after.

To be honest, after having learned other FP languages like OCaml/ReScript, F#, Elixir, Gleam and Elm, I still find Haskell baffling. Not necessarily writing it, but I find reading someone else's Haskell code insanely difficult. It will take many more years before I get comfortable with that.
I agree. I'm quite experienced with FP, but I find Haskell hard, or at least impractical. Not necessarily because of abstractions, but some design choices make programs hard to read for no good reasons.

> but I find reading someone else's Haskell code insanely difficult.

It's true for expert Haskellers as well.

Not to mention large parts of the community using a pointfree, single character variable naming style that's hard to read. If Haskell could consistently be written in a more predictable style, that would be much more suited to collaboration IMO.
> Not to mention large parts of the community using a pointfree, single character variable naming style that's hard to read.

Actually most of the community calls point-free "pointless".

I know because I actually like point-free and noticed that became a minority opinion over the years.

My experience with real world Haskell has been it is more often too verbose in the style of Java to avoid confirming the "Haskellers use single letter variables everywhere" misconception.

> but some design choices make programs hard to read for no good reasons.

Can you give some examples? I may be able to use it in future Haskell writings or teaching new hires.

Haskell is hard. It doesn't need modifiers.

It is harder for people that are used to program in incompatible ways, but it's hard for people without a lot of experience too, and for people with previous knowledge of different paradigms, and for people used to the functional paradigm but not to its level of purity.

And learning the language is no half the battle. Maybe some 10% if you want to stick with very simply idioms, maybe less.

And if you won't learn the hard idioms at all (instead of just not using them 99% of the time like most people do) is there even a point on learning the language? At most your code will be a bit easier to modify, but you will never get most of the value.

> you will never get most of the value.

Counterpoint: a lot of the value is in a small subset of the language that most people learn first. You don't need to use every feature of the language to really benefit from it, and I haven't met any professional haskell codebase that uses everything.

It is fine to just use what you need, and leave the rest.

What you need is never the bare minimum. It's always the minimum, plus one or two details that make your code usable. And those one or two details are different every time.
All of my sibling posts: please be specific!! Somebody not at your level (and there's more for Haskell than more mainstream languages)... like me... isn't going to get what you're talking about.

> And if you won't learn the hard idioms at all ... you will never get most of the value.

Ok... mention a hard idiom? Even if you don't want to go into detail. We'll look it up.

> [...] reading someone else's Haskell code insanely difficult.

I'm wondering if that's because they used a ton of operators like (.)(.)(.) or :=-=: (just made that up, i hope), or was it because of some deeper reason like too much magic hidden in the monad transformers. Anything concrete you're willing to say will clear some of this haze of non-understanding.

Ok, lets be specific. Lets write a comment to explain this function:

https://github.com/dhall-lang/dhall-haskell/blob/master/dhal...

There is already a comment there? And the implementation doesn't look too hard. I don't feel like this is an example of a hard to understand Haskell function.
How about you try your best to explain all of it? What exactly is that you understand here? What does Optics.rewriteOf do? What's the purpose of Lint.useToMap? How about D.subExpressions ? How does that composition work with the loop function?

Even better. How would you go about finding the source code containing the definition of the function `D.subExpressions` ?

No I didn't understand every sub-function in this function. That isn't necessary to get an idea of what a function does. All of your questions can be answered incredible quickly though using https://hoogle.haskell.org/

> What does Optics.rewriteOf do?

Hoogling show this is an alias of https://hackage.haskell.org/package/lens-5.1.1/docs/Control-....

> What's the purpose of Lint.useToMap?

Hoogle again shows https://hackage.haskell.org/package/dhall-1.41.1/docs/Dhall-.... If your function is not indexed you can look at what is qualified as `Lint` and look it up that way.

> How about D.subExpressions ? How does that composition work with the loop function?

https://hackage.haskell.org/package/dhall-1.41.1/docs/Dhall-... All nicely documented... I'm not sure what you mean by composition with the loop function. First the loop function is executed and then afterwards over the result the expression in the first argument to fmap is applied. There is no weird interaction going on here. It's just run this over the result of the loop function if it didn't produce an error.

> Even better. How would you go about finding the source code containing the definition of the function `D.subExpressions` ?

Use hoogle or just look at the imports at the top of the file. Just like any other programming language. Or even better, use the language server to find it for you. For example there is an import here:

`import qualified Dhall.Core as D`

Now you know that function is in the `Dhal.Core` module. Going to that module you see the function is not actually there but is exported from the `Dhal.Syntax` module. It took me literally 5 seconds to find it: https://github.com/dhall-lang/dhall-haskell/blob/master/dhal...

> Hoogling show this is an alias of https://hackage.haskell.org/package/lens-5.1.1/docs/Control-...

That explains nothing? What does it do? How does it do it? What do the type variables `a b a b` mean? Why are there 4 of them? What is ASetter?

Oh the documentation of Setter is fun:

https://hackage.haskell.org/package/lens-5.1.1/docs/Control-...

Absolutely nothing explained whatsoever. Yup, sounds like like the typical, familiar Haskell experience of zero useful docs where you need them.

This argument is literally “the documentation for this domain I don’t understand is bad because I refuse to learn the domain”.

I use the lens library all the time and those docs make perfect sense, particularly if you apply just the teensiest bit of logic and thing “Could a setter, perhaps, set something?”. It’s a DSL, and you don’t understand the domain - I’m sure you’d have a great time explaining this function from LLVM without referring to any other part of the documentation to give it context https://llvm.org/doxygen/group__LLVMCCoreValueInstructionGet.... What’s a GEP? Under the rules you’re applying to the lens documentation, I’m not allowed to look that up, because it should be immediately obvious.

Yes they actually document which bit is what

> Get the source element type of the given GEP operator

Notice the relentness repetition that provides precision and clarity? I now know the argument GEP is a GEP operator. Might seem redundant, but it allows a new reader to start from any point and find their way to the relevant terms / nouns and their types.

The documentation of lenses is geared towards people know what lenses are. Of course this single fragment of documentation doesn't make sense if you don't know what lenses are. If you want to understand what lenses are the package links to a helpful wiki and tutorial: https://github.com/ekmett/lens/wiki/Overview

All within reach within seconds of discovering the function. To understand any tool you must understand the libraries and tools it to some degree. Your questions here are not Haskell specific, and the answers to the question you asked are easy to find. I'm not familiar with the Dhall code base and was able to answer you within minutes.

Documentation for people that already know what the thing is is called "useless documentation".

Its not within any reach whatsoever. The overview isn't linked anywhere in Setter.

Rule of writing documentation: if you're not going to write anything useful in the docs from a user perspective, don't pretend to write any docs at all.

Oh, and also, that link you posted still doesn't answer what does `rewriteOf` actually do.

Going back to the original docs of rewriteOf (https://hackage.haskell.org/package/lens-5.1.1/docs/Control-...)

> Rewrite by applying a rule everywhere you can

Rewrite what? In what? By applying what rule? Which type is the type being rewritten here? Which type is the rule? Completely unclear. This is not how you write library documentation, at all. Tutorials and overviews won't cut it - you need clear API docs, which are written by providing unabiguous explanation of the function inputs, function outputs and how they relate to each other.

(Yes, this is tedious and yes it often involves repetition, but it means a ton for actual library usability)

If there was one recommendation I could give to all Haskell library authors, it would be to explicitly assign an unabiguious descriptive alias noun to every argument within the text that describes their function. In the `rewriteOf` case, that would look something like this:

> Rewrite the type (insert type found in the argument list here) by applying the rule (insert type of the rule in the argument list) everywhere you can.

Oh the overview page is even worse for readability

  _1 :: Lens' (a,b) a
is used immediately after

  set :: Lens' a b -> b -> a -> a
which completely messes with anyone who's mind works in a way that makes it extremely difficult when symbols are immediately reused with completely different meanings.

To summarize, Haskell documentation communication is biased against linguistically clear explanations and towards mathematically precise, symbolic "explanation", and those who are used to unnecessarily struggling with the latter style due to their math background are more likely to "make it".

The Lens library sucks even if you're fluent in advanced math and Haskell. In practice when I have to use Lens (usually with JSON), I just code golf it until it works because it's so damn impenetrable.
I couldn’t agree with this opinion less - lens is incredibly elegant, and very consistent. It is a DSL, and you need to spend some time learning the domain, but once you do you’ll find it hard to live without it. If that’s the way you use the library then you haven’t spent any time to learn how to use it.
If you apply `_1` to `set` then it specializes to `set _1 :: a -> (a, b) -> (a, b)`.

Yes its confusing. It sometimes helps to give longer variables:

    _1 :: Lens' (fst, snd) fst
    set :: Lens' structure focus -> focus -> structure -> structure

    set _1 :: fst -> (fst, snd) -> (fst, snd)

You are bumping up against the severe amount of polymorphism at play in the lens library. This makes it hard to build a mental model but is a tremendous source of expressiveness and utility.
> Documentation for people that already know what the thing is is called "useless documentation".

Right... Like the doc of the regex match function in Rust is useless because it doesn't explain regexes. https://docs.rs/regex/latest/regex/struct.Match.html

Or the C# beginSocketfunction because it doesn't explain what TCP is. https://docs.microsoft.com/en-us/dotnet/api/system.net.socke...

Or the go Exec function in the sql package not explaining with SQL or databases are. https://pkg.go.dev/database/sql#DB.Exec

Any function level documentation expects you to know the basics and the contexts of a library. Documentation on that level is most certainly not useless. It's the standard in every language, for every library. I don't feel like you are approaching this from a fair point of view so I will stop replying. Can't convince people who don't want to be convinced.

> I don't feel like you are approaching this from a fair point of view so I will stop replying. Can't convince people who don't want to be convinced.

You asked what's wrong, I assumed you'd like to know what I think. To me it now looks like you wanted to prove me wrong instead.

I summarized plenty of problems, with the biggest one being bad API docs due to lack of detailed arguments and return types description and labeling and bad naming practices in general.

FWIW, I've watched 4 hours worth of video on Haskell lens by SPJ and I still don't find the documentation clear. It could simply be that I'm not clever enough. Well written API docs would definitely help with that too though.

> Right... Like the doc of the regex match function in Rust is useless because it doesn't explain regexes. https://docs.rs/regex/latest/regex/struct.Match.html

At least they name their type variables:

> The lifetime parameter 't refers to the lifetime of the matched text

C# do too: https://docs.microsoft.com/en-us/dotnet/api/system.net.socke... - every function argument is named, labelled and described separately.

I’m sorry, but this is an astoundingly ridiculous point of view. Those type variables CAN’T be named anything better because they could be absolutely anything at all, that’s the point of generic types.

You are complaining that a single function within a library doesn’t describe the whole abstraction the library is built on? Should every single function, operator and type include the whole fucking lens tutorial so you don’t have to go and find it?

I suppose every web library should include an explanation of IP, TCP, UDP, HTTPS, url encoding, compression and anything else that is needed, on every single function too? Christ, I cannot believe how incredibly dumb your take here is. Libraries in every single language provide some kind of preamble in their documentation which covers what abstraction that library provides, and Haskell is no different; the particular library you have chosen to misunderstand is INCREDIBLY general, the documentation is actually amazingly precise, if you have taken the time to learn what an optic is.

I genuinely think you should be ashamed of this opinion, because it shows that you’re both willingly ignorant, and proud to state that fact publicly.

There are ways of making your point that wouldn't descend into personal attacks. Please don't give Haskellers a bad reputation!
Don't worry, I really enjoy being called dumb and ignorant. Its truly the best way to make me stop and listen to the other side so I can find out what my mistake was.
Why should I be ashamed by pointing out poor library documentation practices?

> Those type variables CAN’T be named anything better because they could be absolutely anything at all, that’s the point of generic types.

I was talking about the word "rule" and the verb "rewrite", and was explaining how the Rust documentation is explicit what is what (The type variable 'a refers to <X>). Similarly, the docs here can be explicit about which part is the rule and which part is the thing being rewritten.

I don't like guessing, but I will try and guess which thing refers to which and try to rewrite the doc myself

> ASetter a b a b -> (b -> Maybe a) -> a -> bSource

> Rewrite (ASetter a b a b) by applying the rule (b -> Maybe a) everywhere it can be i.e. everywhere the function returns `Just a`.

Now, for this part

> Ensures that the rule cannot be applied anywhere in the result:

> propRewriteOf l r x = all (isNothing . r) (universeOf l (rewriteOf l r x))

I would hazard a guess to try and translate that to

Ensures that the rule cannot be applied anywhere where the rule function returns `Nothing`

Did I get these right? Hell if I know. Maybe the Setter is the rule. Maybe that `universe` crap means something else, because sure, the best way to document a function is to describe it with code using yet another poorly documented function (not).

Why are you so defensive? Are haskellers incapable of learning from any negative feedback?

Am I willfully ignorant by pointing out how the documentation can be improved, and by giving examples how other languages take the approach I'm describing? Well written library documentation describes all input and output types explicitly. That's it, thats my claim. Do you contest that claim? Is it impossible to specify which part of that type is a rule and which part is the thing being rewritten, in the docs? Why? Do you think that's good documentation practice?

To answer your questions explicitly

> You are complaining that a single function within a library doesn’t describe the whole abstraction the library is built on?

No, I ask that the documentation describe which nouns refer to which parts of the type

> Should every single function, operator and type include the whole fucking lens tutorial so you don’t have to go and find it?

No, they should include enough description to know what every part of the type refers to in the documentation text.

> I suppose every web library should include an explanation of IP, TCP, UDP, HTTPS, url encoding, compression and anything else that is needed, on every single function too?

No, but it should explicitly state which part of its arguments refers to which concept (IP address, URL, etc), except perhaps when the argument name contains a reference to that concept and the name is clearly visible in the documentation

> Christ, I cannot believe how incredibly dumb your take here is.

Did you actually read my take, or did you knee-jerk into the typical "OMG YOU CAN'T DESCRIBE TYPE ARGUMENTS THEY'RE ABSTRACT" reaction? Yes, I know those types don't have meaning outside of a context. No, that is not what I was asking. (Although, poorly named positional type arguments are pretty dumb, but thats a story for a different day)

> Libraries in every single language provide some kind of preamble in their documentation which covers what abstraction that library provides, and Haskell is no different

They also make the connection between the argument types and the nouns used in the documentation. Something that is made more difficult in Haskell because the argument names are omitted from the generated documentation and often poorly written.

> the particular library you have chosen to misunderstand is INCREDIBLY general, the documentation is actually amazingly precise, if you have taken the time to learn what an optic is

I've spent anywhere between 6-12 hours on lens tutorial and optics, 4 of which on the...

The density of that code is just insanely high. In most language that single fmap line would likely be at least 5 lines of code with lots of variable names to aid in understanding. The map rewrite traversal and actual "compiler" loop part would be separated for sure, and `(D.alphaNormalize (D.normalize expressionType))` would be assigned to another intermediate variable as a "preprocessing step"
Here is the documentation I would write for a function like this, in a normal language:

  {-| Given `Conversion` options and a Dhall type `ExprX`, 
      try to convert the JSON value `Value` to a Dhall expression of the specified type. 

      Additionally, if the conversion is successful, apply a rewrite on all maps in the key-value map pair format to map nodes 
      (i.e.  `[{ mapKey = "foo", mapValue = 1 }] -> toMap {foo = 1}`) -}
And here is how I would decompose the first part

  dhallFromJSON (Conversion {..}) expressionType value = do
    let normalizedType = D.alphaNormalize (D.normalize expressionType)
    dhallExpression <- loop [] normalizedType value
    -- Rewrite maps
    return $ Optics.rewriteOf D.subExpressions Lint.useToMap dhallExpression
Apologies in advance for the meta comment, but: could you just reply to the comments you're replying to, instead of their parent? Trying to find the context for your quote extracts is more difficult than it needs to be.
> IMO, once learned, the type checking is so helpful, everything else seems hard.

I've noticed this claim, that Haskell's type system prevents all bugs, eg at:

https://www.costarastrology.com/why-haskell/

And when they say it prevents all bugs and has no illegal states they seem to mostly mean it has ADTs.

But who is keeping me from having bugs in my numeric code? Not Haskell, making you type "fromIntegral" doesn't prevent bugs. Idris or Ada, maybe.

You can use the LiquidHaskell plugin to perform extra compile time checks to numeric code.
Was that my claim?

It certainly doesn’t prevent all bugs. The refrain I generally see (which I did not write here) is “if a Haskell program compiles, it probably works" Note that “probably” doesn’t mean “always” and “works” isn’t a synonym for “is absent of any and all bugs”.

Yes algebraic data types and compiler options that force you to handle all cases are a big part of it. Additionally, verifying correct record field names, needed type class instances exist, a total absence of nulls (and the forced handling of Nothing cases).

It’s also the type inference, I want the above features and I don’t want to write out type signatures before I’m ready.

The dependently typed stuff (e.g. Idris) is compelling but you do lose the inference.

I’m currently a bit overweight, like maybe 10-15lbs. This is because I have some combination of bandwidth-consuming and focus-consuming things going on that results in me not doing an obviously correct thing: get the fuck back in shape.

Every hacker in the peanut gallery deep-down knows that they should know Haskell and Lisp well. They resent the fact that their job or personal life or whatever doesn’t leave room to get serious about computation theory. And they vent this frustration endlessly on people lucky enough to have had the bandwidth at the right time to thoughtfully contrast computation with physical computing machines (the only person I know to stomp Lemire in AVX is a GHC hacker).

I’m kinda fat because I haven’t fixed it yet. People don’t know Haskell and Lisp well because they haven’t fixed it yet.

Neither is an appealing look.

Meh. I have had the time to learn a fair amount of Haskell and do a few projects in it (mainly interpreters). It's a fun language in some ways, and it was nice to know how to use it because it expands your ways of thinking, but beyond that it's not terribly useful, and I don't think that it's much better at that than other functional programming languages. Learning about the type-system is neat but IMO not as perspective changing as "getting" purely functional programming.

However, as an actual tool to do actual work, I really don't see why you would go about it for anything but very specific projects.

I seriously suspect there is a bimodal distribution are play here, because I would love to use Haskell, or something like it that's possibly more powerful like Idris, wherever I could. And at the same time I notice a lot of people expressing your sentiment.

These systems make it easy(er) to model concepts in a mathematical way and this is my natural (or preferred?) mode of thinking. And they enable you to do it easily and ergonomically. What is there not to like?

(I know there are flaws at hand, even some potentially very debilitating flaws depending on your use case. But damn, I'd use these languages wherever I can.)

See the thing is, I also love thinking about things mathematically/as relations/as recurrences. But I like the more imperative way of thinking about algorithms and data wrangling just as much.

So in practice, I'll go for a language that allows me to do both easily, instead of going for something like Haskell where any imperative way of doing things is very painful (as well as other issues, of course). So why would I use Haskell and limit myself for fairly marginal benefit?

How is the imperative way painful in Haskell? Nothing stops you from just writing everything in the IO monad if you feel your algorithm is easier to write in an imperative style.
What's the point of using Haskell if I'm going to do that? I much prefer using a language that does imperative programming very well and functional programming fairly well if I'm going to use both.
Because Haskell is a general purpose programming language. It doesn't do imperative programming worse then for example Python or C#.
That's very false. Python or C# are significantly better at imperative programming.
The point is you're going to have parts of the program where you won't be doing imperative style at which Haskell will excel, including at combining and composing the imperative and non-imperative components.

There is also the saying that Haskell is the world's best imperative language, and that is not without reason, since you can re-use its usual power of abstraction while writing imperative code too.

I completely disagree that Haskell is a good imperative style language. It's barely adequate by today's standards.

It's awkward, it has a bad ecosystem, it has just as many issues with hidden state, etc...

The only reason you would want to write imperative Haskell over a modern imperative language is the typesystem, but even that can be an issue sometimes, and I'd risk saying that the Rust or C++ typesystems while being very slightly less expressive are better at scale overall due to their flexibility imo.

Another big one is that it really sucks to debug. Seriously, it's brutally slow compared to what you can do with a good debugger in other languages.

If you care about performance, Haskell can be significantly worse than others, both lower level compiled languages (lazy evaluation, large amounts of trash to be collected by the GC, cache trashing, etc...), and higher level interpreted languages, due to the lack of native accommodations to bind into fast code, or even into GPUs.

There are a lot of disadvantages to Haskell if you're going to do mostly imperative code.

I have to disagree with vm this, Haskell is by far the most powerful imperative language I’ve used, because it allows me to abstract what I mean by imperative programming.

I don’t have time to go into details but nearly every statement you’ve made here is in my opinion wrong, other than the difficulty in debugging (and there are very good reasons why this is difficult).

I’ e never found it hard to write fast Haskell, because I put in the effort to learn how to do it. People forget they also put in the time to learn how to write fast C++, and Java; it’s a very large portion of any software engineering curriculum, it’s literally what algorithms is all about. People find writing fast Haskell hard not because it is fundamentally hard, but because they were never taught how.

> due to the lack of native accommodations to bind into fast code, or even into GPUs.

I have no idea what you mean by this, GHC provides a very good FFI, and there are libraries for binding to Rust, R, Objective-C and others. We also have Accelerate for data processing on GPUs, which includes runtime compilation of native code for both GPUs and CPUs.

Have you tried?
Yes, what kind of question is that? Why would I say this if I didn't do it myself. I have written many imperative style functions in Haskell. It's basically required if you are doing things with files, network or Hardware interfaces.
We're not talking about functions, rather about larger programs where most of the code has to be imperative.
What are these programs where most of the code has to be imperative? That seems like a design decision, not an inherent constraint for most programs.
Haskell is a great language for imperative programming, actually. You are not limited by the language in that regard.
People say that a lot, but it's not my experience. Your imperative code will be less readable, an order of magnitude more difficult to debug, and it's going to be much harder to leverage the ecosystem.
Our experiences differ, then. I have found it to be more easy to refactor and parallelize that other languages I have used.

Debugging has not been difficult either, but I have always use print debugging in every language, so there is not much I can compare.

For me personally, it is because the benefit is not fairly marginal nor do I feel like limiting myself when using Haskell, not even for imperative parts of the program.
> Haskell code has the unfortunate property that the types of the functions are harder to understand than their implementations.

Interesting point. What is the purpose of type-declarations? To make implementation easier to understand, I think. When you know (and understand) the type of a component or function you know how to use it, and how to not use it, and thus how to write code that uses it and does what you think it should do.

But if types are harder to understand than the implementation, then types don't help much, do they?

On the contrary, when you can move your complexity into the type system, the. You have just taught your compiler how to check that you are using things correctly, and that is insanely useful in large scale software.
> These systems make it easy(er) to model concepts in a mathematical way

I feel this is largely a lie. The type system is still a crippled language that is also incompatible with the runtime language. It possible to do some very specific things in it, but does it help you describe a system's data flows? Memory allocation patterns? GUI layout engine?

I think the solution is: Just use regular code, it works best. Use types to describe interfaces. Simple types, because with anything else you're painting yourself in a corner.

I think it is largely not, though it's not something that is a magical solution to all modelling problems. It probably depends on the domain you're solving problems in, but on the other hand, I've written CRUD applications in Haskell successfully and would characterize it as a nice experience.

> It possible to do some very specific things in it, but does it help you describe a system's data flows? Memory allocation patterns? GUI layout engine?

I feel you're underselling quite a bit when you say very specific things, but again, perhaps this depends on the problem domain, your thinking style, your programming style or some other undetermined factor. Out of the things you mention, I would say a definite yes to system's data flows, weaker yes for GUI layout and no to memory allocation patterns, because that is a known weak point.

> it’s not terribly useful

Well, it’s been paying my wages for the past eight years, so it’s incredibly useful to me. Are we services “very specific projects”? Are data processing systems? Are geospatial data munging? Are financial systems? Those all sound like mundane, every day projects, and they’re exactly what I’ve been working on.

And when it comes to “actual work”, that’s where Haskell truly shines - being able to fearless refactor code and iterate until the compiler is happy again is my favourite feature of Haskell. It’s not the “neat type system”, it’s the ability to not have to keep the entire project in my head at absolutely all times because I might forget to make a stupid change the compiler should have caught for me. The productivity is massive, once you’ve invested the time, but it does take time and it is an investment.

I've worked with Haskell professionally. It was... interesting. I'd rather be in shape than deal with Stack/Cabal/Nix again. If anyone reading this is wondering if they should learn Haskell - wonder if your time could be better spent having a fun and active life instead lol.
Oh I feel you on the weird effort/payoff curve. I spent months wanting to throw my computer out the fucking window learning Nix.

But I eventually cracked the code and now it like where is deep enough to bury anything running Ubuntu. Let’s fucking break non-dynamic linking of libc because GNU.

NixOS is fundamentally wrong on a few key points. But it’s close enough to right to make everything else look ridiculous. Who doesn’t love never really knowing without rigorous audit what is running on my machine that’s highly optimized for Drepper’s career.

I second this. I think Haskell is a research programming language, and in a way I feel lucky that I first learned it way back in 2002, long before anyone seriously thought of using it for professional software development.

Haskell 98 is a pretty awesome programming language. It's not the best fit for every domain, but you can write concise readable code in it. The type system is just sophisticated enough to let you model your domain, but not so sophisticated that it becomes a distraction.

A lot of modern Haskell code has the unfortunate property that the types of the functions are harder to understand than their implementations. Type inference is much more valuable when it's the other way round. You know that [a] -> [a] is the right type for the 'reverse' function, so if you get a type error, you're sure that you made an error in your implementation. Conversely, many modern Haskell libraries give rise to extremely complex types, even when you're doing something mundane like making an SQL query. When you get a type error, it's then anyone's guess whether you misannotated a type somewhere or made an error in your implementation.

I personally feel that partial types systems are the way forward. Type the 80-90% of your code that's easy to type without a fancy type system. Dynamically type the rest and write some tests for it. Fancy types make you feel smart (and in all seriousness are a fascinating and important research topic), but their practical utility is limited IMO.

I agree that gradual type systems are great, but man, I feel like the expressiveness of e.g. Typescript is fantastic even for 'normal' programming.

Something like fully typed forms (so that e.g. <Input type='text' path='address.street' /> only works if your form data has a { address : {street : string} } + branded types for more custom data fields) is absolutely magical and makes working with lots of highly complex forms a breeze. Tests just don't compare.

To take your analogy further, the LISP masters are like ultra-fit personal trainers. When they run a bootcamp for out-of-shape people, they start off with impractically intense exercises and just make everyone demotivated.
"Oooooooh! Welcome to Rex Kwan Do! Let me show you how superior I am to you know-nothing chumps! Bow to your sensei! Bow to your sensei!"

I had some pretty bad language instruction in college, but the professor teaching Haskell was the worst. Maybe it really was a case of "if you can't explain it simply, you don't understand it well enough.", but I remember everyone finishing that class (just barely) and nobody ever wanting to do use it again.

To present anecdotal counter evidence: my FP class using Haskell was phenomenal. I've talked to other students who've also taken the class and they generally agree.
Learning Haskell is a mind makeover I agree. People are missing the point when they say nonsense like, "well if haskell is so great why isn't every piece of software written in it and outperforming on all possible metrics?"

That kind of lazy thinking "isn't even wrong." You learn haskell to become the kind of person who "has learned."

The ignorant imagine haskell is a promised superpower that will under-deliver "productivity gains." It's a new way of thinking and building software.

If there are productivity gains they will be invisible: you wont make the first feature X twice as fast (dev time), but you might make the 100th feature twice as fast due to lack of tech debt, but you wont notice as every team gets used to their cadence.

The lack of tech debt comes from there not being the horrible OO sins. OO is great but there is a lot of “assume not null” and L in SOLID violations in practice and it all adds up.

Haskell has explicit opt-in nulls (optionals) and there is no L to worry about as there is no subtyping.

(comment deleted)
> I’m currently a bit overweight, like maybe 10-15lbs. This is because I have some combination of bandwidth-consuming and focus-consuming things going on that results in me not doing an obviously correct thing: get the fuck back in shape.

Luckily, this doesn't take as much time as it sounds, because exercise (and other time consuming things) aren't helpful for losing weight. Everything else yes, losing weight no.

That's because it's pretty much entirely about your diet, and exercising makes you hungrier, which makes it harder to comply with the diet.

I no longer think all kinds of gateways to Haskell is actually a good thing. Once you go through that gateway you are most likely to get frustrated and go right back, as evident from other replies. People say it's enforced purely functional programming is difficult to grasp but I write mostly pure FP in Scala and I get frustrated with how difficult it is to build something practical in Haskell
I've seen Dhall mentioned on HN front page twice this past week. Is anyone here using it regularly? How has your experience been?
> Take Kotlin, for example. It has so many escape hatches that someone new to functional programming can end-up writing what is essentially an OOP/imperative style Java program, merely in Kotlin syntax.

That's... literally the entire purpose of Kotlin.

It is, and the author isn't criticising the language as a whole for it. They're merely commenting that it isn't a good options for forcing you to use and therefore learn functional paradigms.
I prefer the pragmaticality of Rust, or even it's father Ocaml, than the Over-the-top-abstractly Haskell.
I was about to ask how you would describe OCaml as Rust's father, but I get what you mean actually.
Also the Rust compiler was originally written in OCaml. Not that that necessarily implies a parent-child relationship between the languages, but it's clear OCaml was on the minds of the original Rust developers.
But what is Dhall? Ok yes, something that helps you write config files because Kubernetes is a pain. I am not a DevOps person so I can't always empathise with the problems DevOps deals with on a daily basis. That something is written in Haskell for Haskell sake is not something I care about. Not that I dislike Haskell, PostgREST is written in it and that is one of the coolest and most productive things I know about, and it does not ask me to learn Haskell (just a lot of SQL).

How does it compare to something like CUE[1] which uses a lattice algebra to weave config files and code together to manage different configuration environments similar to how some computational-algebra-system use pattern matching and rewrite rules to encode mathematical knowledge.

[1] https://cuelang.org/

IMHO cue is generally better for complicated configurations, including Kubernetes. Dhall has some nice features though. In particular the ability to import other files as semantic hashes seems like a great feature.

To manage configuration you also need a system to understand the state of the configured system so that when you delete a configuration you can reconcile that with the system. So then it becomes more practical to use Pulumi with TypeScript. It would be nice if there was a separate state reconciliation system that one could adapt to use with Cue or Dhall or any other frontend.

since you mentioned Kubernetes...

> It would be nice if there was a separate state reconciliation system that one could adapt to use with Cue or Dhall or any other frontend

this exactly was thinking behind https://carvel.dev/kapp for Kubernetes (i'm one of the maintainers). it makes a point to not know how you decided to generate your Kubernetes config -- just takes it as input.

> In particular the ability to import other files as semantic hashes seems like a great feature.

it's an interesting feature but seems like it should be unnecessary given that config can be easily checked into git (your own and its dependencies).

kapp looks good! I wish that had been available (or I had known about it) back when I was using cue. But kapp is k8s only whereas Pulumi has cloud provider backends as well as k8s.

> it's an interesting feature but seems like it should be unnecessary given that config can be easily checked into git (your own and its dependencies).

Backend provider functions need to be imported for example. The semantic hashing system makes imports easier- if just a comment is changed, there is no change. There's a lot of interesting possibilities here to help audit changes.

Skip the blog and look at the language landing page: https://dhall-lang.org/

The blog article talks too much but the language itself seems reasonable. Statically typed config languages sound like a neat idea to pursue, although I'm not sure how promising this particular one is.

I recently tried to get into Haskell, while also looking for relevant projects written in it on Github and couldn't find anything "interesting" (this may vary from person to person, obviously).

I was watching a YouTube playlist but I'd rather read something like "Haskell by example", or some sort of small apps with comments to understand how it works.

I was interested in writing some binary parser in it but I failed miserably.

Actually writing a self-contained parser (in essence a function parse :: [Binary] -> AST) is one of the things where Haskell shines. You could use `attoparsec` (and to start off, even using the stdlib's https://hackage.haskell.org/package/parsers-0.12.11/docs/Tex... can get you very far).

And I say that as someone, whose top-rated comment in this thread is more or less a Haskell rant.

I think OP's point was that THEY failed miserably. Presumably, they reached for Haskell to begin with because they knew it's supposed to shine for things like writing parsers. Maybe they were making a point about learning curve or FUX with Haskell...not sure since OP didn't elaborate.
That's cool to know, maybe I should keep trying.

And regarding your top comment, I agree that's quite hard to find interesting SW written in it, that's one of the reasons I quickly gave up, but I guess it doesn't hurt to learn about FP after all, I want to at least write something that works before finally decide if it's worth it or not.

I’ve been thinking about producing a video series giving an introduction to FP from the ground up.

However I do stream myself working on non-trivial Haskell projects from scratch to give folks an impression of what it’s like work in this language once you’re comfortable with it.

I do enjoy taking digressions to answer questions too, so if interested check out https://twitch.tv/agentultra

> I recently tried to get into Haskell, while also looking for relevant projects written in it on Github and couldn't find anything "interesting" (this may vary from person to person, obviously).

Let me try covering interesting for many people (hopefully including you!):

Start: https://github.com/topics/haskell

https://github.com/koalaman/shellcheck

https://github.com/PostgREST/postgrest

https://github.com/hadolint/hadolint

https://github.com/jaspervdj/patat

https://github.com/smallhadroncollider/taskell

https://github.com/fosskers/aura

https://github.com/HuwCampbell/grenade

https://github.com/Haskell-Things/ImplicitCAD

https://github.com/lettier/gifcurry - GIF editor

https://github.com/2mol/pboy - pdf management utility

https://github.com/ChrisPenner/rasa - Extremely modular text editor built in Haskell

https://github.com/samtay/tetris - terminal interface for Tetris

https://github.com/mattgreen/hython - Haskell-powered Python 3 interpreter

https://github.com/lettier/movie-monad - A free and simple to use video player made with Haskell.

https://github.com/dbousamra/hnes - NES Emulator written in Haskell

Well clearly I suck at 'searching' :-) for some reason I thought searching on github `language:haskell` would spit useful results, but now I can see I did it wrong.

that's a good list to look at into for inspiration, thanks!

A few more:

https://github.com/simonmichael/quickbench - Easily time one or more commands with one or more executables and show tabular results

https://github.com/simonmichael/shelltestrunner - Easy, repeatable testing of CLI programs/commands

https://github.com/simonmichael/hledger - Robust, fast, intuitive plain text accounting tool with CLI, TUI and web interfaces

https://github.com/haskell-game/fungen - A lightweight, cross-platform, OpenGL-based 2D game engine in Haskell

https://haskell-game.dev - a small selection of the many games written in Haskell

https://haskell-via-sokoban.nomeata.de - Haskell via Sokoban

https://lhbg-book.link - Learn Haskell by building a blog generator

https://github.com/input-output-hk - the Cardano blockchain and Daedalus wallet

https://github.com/jgm/pandoc - Universal markup converter (already mentioned above)

https://github.com/simplex-chat/simplex-chat - secure messaging platform

https://www.libhunt.com/l/haskell - more projects...

I love Dhall, but I think Optionals might not be a good fit for a configuration language, especially one that has to deal with Kubernetes

A Dhall joke (those that write a lot of it will get it):

Some foo walks into Some bar and asks the bartender, do you have Some beer? The bartender answers: We have None.

One nice thing that can be done with pure functional programming (not sure what - if any - languages or runtimes implement it) is the caching of the results of expensive calls.

If there's any escape hatch to a method/function there's no guarantee that the result of f(q) will be a constant.

On escape hatches: being able to do imperative style processing is great. A loop-and-mutation is often a much more readable way to write what could be written as a fold or (worse) some tail recursive method. F# does this very well I think, with imperative code sitting quite naturally inside an ML dialect.

More importantly, it allows describing the how (or at least something more closer resembling the how) of the computation rather than just describing what is being calculated.

Are Stack / Cabal written in Haskell ? If so I dont think thats a very good advert for the benefits of writing software in Haskell - the quality as an uninformed user would appear on par with a crappy Perl script.
Written by and for academics and/or others who don't have to ship software it seems
>Learning Haskell directly can be a very frustrating experience for all, but the most headstrong. Trying to directly introduce Haskell to a large team is almost guaranteed to fail.

>However, there are a few languages, heavily inspired by Haskell, that have a much gentler learning curve.

There are also functional programming languages which are not inspired by Haskell and which are easy to learn. I quite enjoy F# and was able to pick it up relatively fast.

Also, about Haskell being hard, it still doesn't seem "rust" hard.

After working with Kubernetes and Helm I have learned to hate mustache-style templating and be generally in-easy when working with yaml in general (though yaml Because of this I've started using Dhall in personal projects. It feels like the slimmed down functional thing you want with useful features you want when writing configs for example. I could even see it useful in other templating situations as well, which I am experimenting with but don't know for sure yet.
Thanks for this comment. I'm currently introducing K8 at my company and thought about using Helm at first, but since I don't have too many services at this point, I fell back to standard yaml config. This confirms my choice for now. I'll look into Dhall instead.

I skipped Helm since I felt it introduced too much new tooling on top of K8 which is also new in company and me. But then I also felt the issue of duplication quite immediately.

Dhall is in some ways actually harder (at least, stricter) than Haskell, I think. The fact that there's guaranteed completion means the code has to have complete integrity. If you've defined a union type, then anytime you define a polymorphic function that takes that union type as an argument, you need to both handle all of the union type's actual types, and handle them properly. So even if you as the developer know that that one of the types is never going to actually be passed to this function, you can't just throw an error or set that scenario as undefined.