Man, whatever you're working on must be incredibly interesting and difficult to necessitate a language with the advanced tools necessary to translate even the most abstract of thoughts into code.
Now, if you'll excuse me, I have to write a web app that accepts this API and returns these things... and implement auth... sigh.
I was very, very obsessed with Haskell for about 9 months, during which time I attempted to make a very simple web app at least 5 times. I like debugging, but I just flat-out couldn't make it work. Plus, constantly feeling stupid is a huge drain on one's motivation to continue. Plus, the community was... not helpful. I mean, I get the whole "Avoid success at all costs", to avoid the language becoming entangled with the desires of large corporate interests, but it did feel a little extreme. Mind, this was 7 years ago, and I assume haskell stack and a few other tools make things a lot easier.
Actually, this is something I've always wondered: I know that any Haskell enthusiast would, upon hearing someone say, "I like the ideas, but it's just too hard to program in", respond, no, it's not, you just didn't learn it the right way, or this IDE with that extension makes it much easier, or you really should have started with Learn You A Haskell. But what I'm really curious about is this: Putting all that said, assume for the moment that Haskell really is too complicated for anyone without an advanced degree in hyperbolic topology to understand.
I like to use my complexity budget on the problems I'm solving, not the tools I'm using. Haskell has been around for 30 years, sucking people in and spitting them out again.
So I'm guessing you write all your code in brainfuck given that it has possibly the lowest complexity of all PLs?
Tbh this complexity budget is a rather stupid notion in regard to programming languages. Higher level languages allow you to spend less cognition on the language provide you actually know the language. There's a very clear reason we are moving away from C to more complex languages.
There is more cognitive overhead to bf, the programming language does not help you mitigate it.
C++ destructors and rvalues help condense ownership without having to think about the execution path of a whole program.
I'm not aware of what haskell features + tools pragmatically decrease the complexity of a program. If an experienced programmer is still fighting with the language heavily after 9 months and can't figure out a good way to debug programs, that should be a red flag if it is a consistent scenario.
GADTs are insanely helpful in writing correct by construction code. That function indicate the effects it has in the type is possibly the greatest feature Haskell has. Haskell LSP is great allowing to write and evaluate doctests in real-time.
Haskell can be learned in a couple of months of serious learning. If you don't get it after 9 months you simply weren't serious in learning it. It's not like Haskell is harder then any other language. It's concepts are different which makes it hard to learn. But C++ and Python are much harder to write code in then Haskell.
> If you don't get it after 9 months you simply weren't serious in learning it.
That seems a bit harsh. I can believe that everyone should be able to learn Haskell sufficiently well after a couple of months if they stumble across the correct learning material and have sufficient support. I can also see that someone from outside the community might make a solid effort for nine months and not make much progress, simply because the correct learning material wasn't easily discoverable.
To be clear. Serious in learning it would mean dedicating at least 8 hours a day to it. By for example starting a Haskell job and needing to learn the language. Besides I don't really think material is a valid excuse anymore given that there are so many tutorials and books available. There are also many chats and mailing lists where you can ask any question you might have that are beginner friendly.
Given that I don't have a degree in hyperbolic topology, that would definitely put a damper on my appreciation for the language, yep haha. I'd probably consider myself to be on the lower side of average intelligence for programmers, and I like it a lot. Granted, I haven't gotten around to writing a working web app in it yet.
haskell has a long history, and ancestors with a long history too, it's normal to feel alienated when you have a background with mainstream languages
I can't talk for haskellers (I'm not using it) but it's not that FP/haskell doesn't care, it's just that its medicine taste sour to you.
If I can give you another point of view, languages like php, started very differently from what they are now. Give a 'closure' to a php3 dev and he will feel just like you do I believe. In 2020, the most used php web frameworks use closures as the normal way, and people are quite happy with it.
I believe haskell is just like that, it's just too advanced.
I don't know what trouble you ran into. It's not difficult to write a web app in Haskell, nor was it seven years ago. All the major frameworks had been established by then. But at the same time I don't want to dismiss your difficulties. Whatever was going on clearly left a scar.
I will say there's no such thing as a programmer who can't learn Haskell and be productive in it. It's not fundamentally more difficult than any other language. It's just that you have to learn to program more or less from scratch again. That initial learning curve was more work than I'd been through to learn a programming language in 25 years, when I first learned Haskell.
Of course, there's definitely such a thing as a programmer who won't like Haskell even after learning it. It's not for everyone. But neither is perl, or c, or python, or java, or....
I can relate. I can program in just about any language. I can handle Lisp, muddle through most Prolog and don't have too much trouble with FPs like Elixir and Erlang. But as much as I have read again and again about Haskell, I always eventually seem to get utterly confounded.
I know and interact with a handful of Haskell devs, some of whom even work with it full time. Some of the ideas behind Haskell intrigue me, but I'm a complete idiot. Now everyone is going to say that you don't need to understand the different theories behind purely functional languages to learn Haskell or know any math, but I'm not quite convinced. Just the vocabulary alone of most Haskell programmers I talk to seems foreign to me. I'm not just talking about half-jokes like zygohistomorphic prepromorphism either. The dictiob used in normal programming discussion makes no sense to me.
I disagree; a powerful language lets you push all the junk aside and avoid repeating yourself. The worst part of writing a webapp in, say, Java, isn't the business requirements, it's interfacing with the ridiculous reflection-based authentication library and threadlocal-based database session management and whatever other crap.
Most of my limited production Haskell experience has been schlepping enterprise JSON using AWS Lambda and calling REST APIs.
I’ve not had any bug reports in ages. Which is counter to what most of the teams were used to when deploying services in JavaScript or Ruby. Usually you test like hell, QA, ship, get a few error reports from users, fix them, get one or two a couple months later and fix those... then a year later you change something and get a bunch of new error reports about the feature that used to work... and you fix those.
Haskell forced me to address most of those errors before my program would compile. I had to write a few tests but orders of magnitude fewer than I was used to. I shipped fast.
I got one report from the customer many months later when I made a mistake due to a miscommunication of requirements. One test case prevented that kind of error happening again inadvertently and shipped in less than an hour. Haven’t heard any reports since.
Haskell is my go to where I can get away with it. Absolutely solid stuff.
I have quite a personal interest in learning more about pure FP, type systems, theorem provers and all the hot stuff.
But: you're also comparing Haskell to completely dynamic languages like Ruby or JS. There are things in the middle. Even Java lets you avoid many silly typing errors and if you use something like Kotlin (and you use it well, not just as a slightly more modern Java), you can IMHO get quite far in terms of correctness for typical web backends.
Now the power of languages like Haskell may be warranted in more complex scenarios and I don't want to claim there are no benefits to it, but there are degrees in between the madness that is JS and something like Haskell.
> I realized that there is no way to take a value out of an IO purely, that is, you cannot have a function IO a -> a. It is not because IO is a Monad and Monads are special cased magic, but simply because the constructor of IO is not exported out of its module.
That's not really why. Even if you could unwrap the IO constructor, there is no `a` inside it!
As it turns out, with you can unwrap the IO constructor if you import GHC.Types from ghc-prim:
Prelude> import GHC.Types
Prelude GHC.Types> :set -XUnboxedTuples
Prelude GHC.Types> let (IO f) = getChar
Prelude GHC.Types> :t f
f :: GHC.Prim.State# GHC.Prim.RealWorld
-> (# GHC.Prim.State# GHC.Prim.RealWorld, Char #)
What's inside is a function. You can't get the return value out of a function without running it. You can, of course, run it. `unsafePerformIO` will give you a thunk that, when forced, will do just that.
While true (and perhaps is another minor enlightenment, that the parameters of a type signature do not need to actually correspond to any runtime values, which the article talks about in its own way later), I do think this is an extremely important point that I appreciate the article bringing up and want to hammer home myself.
I often see the sentiment on HN and other programming forums that the "magic of Haskell's mysterious and arcane type system with the bogeyman of inscrutable category theory and monads" (to exaggerate just a tad) prevents you from mixing pure and non-pure code in Haskell. That's not what's going on. It's just a clever choice by the standard library to not export certain functions that unwrap a type (or at least hide it behind a function with the word unsafe in it), a trick that is used by any language that has support for private and public functions.
Indeed this exact same thing can be replicated by any language that is expression-focused and has support for immutable bindings. Just modify your standard library to wrap everything side-effectful in an IO type and don't let users unwrap that.
On reflection, I think that's a really good perspective. If you'll notice, my ultimate conclusion was "actually, you sort of can" so it makes a lot of sense to look at it as "you can't because that's not the interface they chose to expose." There are important reasons why that's what they chose, but that's a separate question. (Both of these questions are valid interpretations of the original wording of the question, but they are interesting considered separately.)
There's a lot good in there, but the laziness section is missing one key insight still: you have to think of strictness in terms of evaluation dependency. For Int, (+) is strict in both arguments. That doesn't mean throwing a + b someplace causes a and b to be evaluated. It means that after the expression a + b has been evaluated, both a and b have been evaluated (at least as long as the result is non-bottom). Since it's a direct data dependency, you know a bit more. You specifically know a + b must evaluate a and b before it adds them.
That's not necessarily true with all strict evaluation. You can use seq to create an evaluation dependency between two values that need not care about ordering. If c = seq a b, the when c is evaluated, both a and b will have been evaluated, and c will evaluate to the b. But there is explicitly no ordering there. The compiler may choose to evaluate either a or b first, by whatever metrics it wants.
It takes a lot of practice to get the hang of thinking about evaluation like this. But it's the key to not having memory use problems in Haskell while also not breaking code with unnecessary strictness.
Yeah there are. At the high end I think Jane Street will train you in OCaml when you're a junior new hire, assuming you show the competence and care to figure it out. I imagine you have to show some functional programming experience (using it, not work experience), knowledge, or even aspirations, but I've often heard of the actual practical experience coming from the job.
You can, and I know a few people who work with it, but from what ive heard theres very few positions out there so you hace to be very good as opposed to just an average Haskeller.
The point about let exressions is excellent, and I definitely relate to the author. I think they're underemphasized in FP evangelism to people used to the imperative paradigm - "look, this is pretty similar to how it'd look in python if you were disciplined about not reusing names."
29 comments
[ 3.1 ms ] story [ 73.0 ms ] threadNow, if you'll excuse me, I have to write a web app that accepts this API and returns these things... and implement auth... sigh.
It wouldn't really help, of course. Business requirement drudgery is business requirement drudgery in any language.
Actually, this is something I've always wondered: I know that any Haskell enthusiast would, upon hearing someone say, "I like the ideas, but it's just too hard to program in", respond, no, it's not, you just didn't learn it the right way, or this IDE with that extension makes it much easier, or you really should have started with Learn You A Haskell. But what I'm really curious about is this: Putting all that said, assume for the moment that Haskell really is too complicated for anyone without an advanced degree in hyperbolic topology to understand.
Do you care?
Tbh this complexity budget is a rather stupid notion in regard to programming languages. Higher level languages allow you to spend less cognition on the language provide you actually know the language. There's a very clear reason we are moving away from C to more complex languages.
C++ destructors and rvalues help condense ownership without having to think about the execution path of a whole program.
I'm not aware of what haskell features + tools pragmatically decrease the complexity of a program. If an experienced programmer is still fighting with the language heavily after 9 months and can't figure out a good way to debug programs, that should be a red flag if it is a consistent scenario.
Haskell can be learned in a couple of months of serious learning. If you don't get it after 9 months you simply weren't serious in learning it. It's not like Haskell is harder then any other language. It's concepts are different which makes it hard to learn. But C++ and Python are much harder to write code in then Haskell.
That seems a bit harsh. I can believe that everyone should be able to learn Haskell sufficiently well after a couple of months if they stumble across the correct learning material and have sufficient support. I can also see that someone from outside the community might make a solid effort for nine months and not make much progress, simply because the correct learning material wasn't easily discoverable.
haskell has a long history, and ancestors with a long history too, it's normal to feel alienated when you have a background with mainstream languages
I can't talk for haskellers (I'm not using it) but it's not that FP/haskell doesn't care, it's just that its medicine taste sour to you.
If I can give you another point of view, languages like php, started very differently from what they are now. Give a 'closure' to a php3 dev and he will feel just like you do I believe. In 2020, the most used php web frameworks use closures as the normal way, and people are quite happy with it.
I believe haskell is just like that, it's just too advanced.
I will say there's no such thing as a programmer who can't learn Haskell and be productive in it. It's not fundamentally more difficult than any other language. It's just that you have to learn to program more or less from scratch again. That initial learning curve was more work than I'd been through to learn a programming language in 25 years, when I first learned Haskell.
Of course, there's definitely such a thing as a programmer who won't like Haskell even after learning it. It's not for everyone. But neither is perl, or c, or python, or java, or....
I’ve not had any bug reports in ages. Which is counter to what most of the teams were used to when deploying services in JavaScript or Ruby. Usually you test like hell, QA, ship, get a few error reports from users, fix them, get one or two a couple months later and fix those... then a year later you change something and get a bunch of new error reports about the feature that used to work... and you fix those.
Haskell forced me to address most of those errors before my program would compile. I had to write a few tests but orders of magnitude fewer than I was used to. I shipped fast.
I got one report from the customer many months later when I made a mistake due to a miscommunication of requirements. One test case prevented that kind of error happening again inadvertently and shipped in less than an hour. Haven’t heard any reports since.
Haskell is my go to where I can get away with it. Absolutely solid stuff.
But: you're also comparing Haskell to completely dynamic languages like Ruby or JS. There are things in the middle. Even Java lets you avoid many silly typing errors and if you use something like Kotlin (and you use it well, not just as a slightly more modern Java), you can IMHO get quite far in terms of correctness for typical web backends.
Now the power of languages like Haskell may be warranted in more complex scenarios and I don't want to claim there are no benefits to it, but there are degrees in between the madness that is JS and something like Haskell.
That's not really why. Even if you could unwrap the IO constructor, there is no `a` inside it!
As it turns out, with you can unwrap the IO constructor if you import GHC.Types from ghc-prim:
What's inside is a function. You can't get the return value out of a function without running it. You can, of course, run it. `unsafePerformIO` will give you a thunk that, when forced, will do just that.I often see the sentiment on HN and other programming forums that the "magic of Haskell's mysterious and arcane type system with the bogeyman of inscrutable category theory and monads" (to exaggerate just a tad) prevents you from mixing pure and non-pure code in Haskell. That's not what's going on. It's just a clever choice by the standard library to not export certain functions that unwrap a type (or at least hide it behind a function with the word unsafe in it), a trick that is used by any language that has support for private and public functions.
Indeed this exact same thing can be replicated by any language that is expression-focused and has support for immutable bindings. Just modify your standard library to wrap everything side-effectful in an IO type and don't let users unwrap that.
That's not necessarily true with all strict evaluation. You can use seq to create an evaluation dependency between two values that need not care about ordering. If c = seq a b, the when c is evaluated, both a and b will have been evaluated, and c will evaluate to the b. But there is explicitly no ordering there. The compiler may choose to evaluate either a or b first, by whatever metrics it wants.
It takes a lot of practice to get the hang of thinking about evaluation like this. But it's the key to not having memory use problems in Haskell while also not breaking code with unnecessary strictness.
Do people hire experienced imperative programmers and train them to use languages like Haskell?
Probably many of the same reasons Haskel is not yet a thriving mainstream language.
90% of problem domains yield well to procedural/hybrid languages.
The rest could stand the additional rigor and even provability possible with pure FP languages.