Weird, but Haskell feels easy
47 points by KnorrFG
47 points by KnorrFG
I used it for maybe 2-3 years, and I would have to agree. I don't know the mathematics, and I don't need to know; I can read a type signature and fill out the variables, and typeclasses didn't seem very different from traits or other interface-like features. I think higher-kinded types are less common, but if you know higher-order functions¹ from other programming languages, it's like that but with types I think. Getting to a working program didn't take long, and then parallelizing it was so easy.
That common typeclasses have "mathy" names (which seems to be a recurring complaint) doesn't make a difference to me. They're no worse than those funny Java classes. I wonder if language is a component, as it's all "foreign" to me anyway; SingletonProxyFactoryBean or Applicative, I can't understand either from looking at the words.
That common typeclasses have "mathy" names (which seems to be a recurring complaint) doesn't make a difference to me
FWIW, Matt Parsons wrote an article saying the same, Why 'Functor' Doesn't Matter.
I used it for maybe 2-3 years, and I would have to agree
Me too. I just find it frustrating that there is a lot of accidental complexity, coming from e.g. language extensions and some tooling, that makes it hard to use Haskell in the industry and has given the language a bad reputation.
At its core, Haskell 98 and 2010 are simple languages. I wish an effort such as Simple Haskell took off or, alternatively, an ML dialect like OCaml or F# gained popularity. Living in a world were something like Python becomes the dominant language is sad.
Haskell and the ML family embody lots of good PLT ideas that we are throwing away. We should be building software with good abstractions and guarantees (e.g. refinement types) instead of using mediocre languages everywhere. At least, Rust is gaining some traction and also making lots of verification ideas more mainstream.
While they're not my favorite ML dialects, swift, kotlin, and rust are all pretty popular
I don't think Kotlin can reasonably be considered an ML dialect.
Kotlin is extremely clearly a Java dialect, if nothing else. It arguably takes some inspiration from ML features, but if that were enough to call something a "dialect," then every language would have to be considered a Lisp dialect because modern if/else originated from Lisp's cond!
I really wanted the article to show some code examples of how Haskell was so much easier
Especially with teases like "Clojure was nice but on the other hand some of the constructs I worked with were way too difficult for me to work on."
It's a bit surprising that the author bounced off OCaml first. Haskell is my main general-purpose language, and OCaml doesn't feel too different; if I were forced to use OCaml I'd only be mildly disappointed.
I wonder if the author's improved understanding of the problem has also contributed to the sense of ease foe this new iteration.
OCaml's equational reasoning is weaker than Haskell's; neither language has eta-equivalence but Haskell comes much closer.
As someone who in principle prefers Haskell, OCaml is more practical and also seems to be more lively. The community is doing a great job with effects.
Almost all of these are false impressions. It's not particularly practical for a higher-level language to not have green threads and a preemptive scheduler and not have thread-safe access to mutable values via something like STM. The list goes on, both the runtime and the libraries have been behind in OCaml for decades from a raw practical perspective. I've only ever found that people who've used neither (I've used both) seem to think that OCaml is somehow more practical.
Truth be told, it's also why F# is a better bet than OCaml if you like the language, because at the very least you'll get access to a modern runtime that isn't constantly stuck with some massive missing feature coming soon(tm).
Wasn’t ocaml 5 the big release with green threads and multicore support?
You're right, they do seem to have been introduced. This still leaves things like STM, etc., which is still where Haskell has a decade+ head start. My point is mostly this:
"OCaml is more practical" has been a meme for a long time. It's a meme because people were saying this when OCaml was literally a single-threaded runtime with a global interpreter lock, with almost zero libraries for doing anything at all. I will concede that it's getting better, but the truth is that being "better" was never the reason for this outlook on OCaml vs. Haskell, it was essentially just programmer lore that drove this meme. Throughout OCaml has had a tenth of the users, a tenth of the jobs, etc. of Haskell but you wouldn't know it from listening to this lore on the Internet.
Googling for ocaml stm immediately brings up kcas, an stm library for ocaml.
I’m interested in where you’re getting your user and job numbers from?
Googling for ocaml stm immediately brings up kcas, an stm library for ocaml.
kcas would do the trick if it could constraint effects, but as per the README it cannot. STM is one of the few places I think bothering with restricting effects is actually crucial:
let enqueue_and_log ~xt queue message =
enqueue ~xt queue message;
(* BAD: The printf could be executed many times! *)
Printf.printf "sent %s" message
For STM code to be guaranteed to be well behaved you want runtime support and compiler support for guaranteeing you're doing only retryable things in your transactions, i.e. not just arbitrary code. In this way OCaml is a lot like all of the other languages (C#, Clojure, etc.) that "have STM" in that they technically do have something like STM, but it's usually a meh implementation and with no guarantees with regard to it resulting in well behaved programs for you.
The job numbers are my own observations from EU & American job markets over the years. It's been 3-4 years since I checked either of these languages now because I have on interest in using them in practice, but the results were fairly consistent over a 5 year period.
I guess untyped effects just wouldn’t fly in Haskell. I do wish there was a canonical (ie in base) effect library.
This seems like a data modeling and compilation problem, which is where Haskell absolutely shines. When I left the language some years ago, the big sharp edges were around effects (handled via monad transformers at that time) and error handling. I know there's been a lot of research in those areas in the intervening years that seems to have made the situation closer to the excellence of using it for data modeling and compilation, but I haven't spent the time to see how close.
I really think that the category theory is more of a distraction unless you're trying to design new algebras.
Good reminder that the comment section reflects commenters, not reality.
What is that supposed to mean?
Aren't people's experiences also part of reality? Or are you using a more constrained definition of "reality", meaning something more akin to "what the software industry at large believes" or "what is common wisdom"?
Sorry if this comes off as confrontational, but it's a sincere question. I really don't understand what your comment was supposed to mean.
It means that reality is bigger than what people on the internet say. The comment section loves to say all sorts of things are hard: compilers, FP, Haskell, getting static typing right. For them, it seems, anything you have to persist at for a bit is just labeled “hard,” which suggests avoiding it. And yet, with time and study, I got to be decent at those things.
I’m not special, I was just interested in those things and that let me keep at them.
Mostly a comment borne of frustration that we should reap the benefits of more rigor/different paradigms without actually putting in the work to get there.
Oh thanks so much for the clarification. I got a completely wrong impression from your original message. Almost the opposite of what you meant.
Found it interesting that the author has such a positive experience with Haskell, since that isn't heard all that often, I thought of it as a nice counter-point to the typical experience.
I would argue you don't really hear about the typical experience, but you probably have heard all of the lore that people make up over time.
There are plenty of issues I have with Haskell, but learning how to be productive in Haskell and how to make applications for "industry" in it is both a solved problem and a very easy one to learn (easy enough that I can teach anyone in a couple of months of free time).
Typical experience with Haskell shouldn't really matter IMO, Haskell works for a very particularly neurology. For some, it's instantly the cosy home their autistic mind was craving their entire lives, for others, it's needless constraints and bureaucracy. The experience report from one type of individual is completely irrelevant for the other type.
I don't think it is fair to conflate neurology and programming patterns, at least reducing it to the point of having the "right mind" or not. I'm not on the spectrum at all but I've always been drawn to Haskell the moment I've heard about it. I'm messy, at least the rigor helps me keep things in place. I wouldn't be surprised hearing autistic people say they find Haskell cozy. But I do too, and so does the author of the article! It's interesting knowing why diverse people like it.
And I wouldn't toss the friction and papercuts people deal with in the same bin either. There is insight in them and improvements to build from this feedback.
IMO, Haskell works for a very particularly neurology...the cosy home their autistic mind was craving their entire lives
You managed to both insult the neurodivergent and the Haskell community in one swoop. That's impressive, especially since you managed to do it with no hint of reality involved.
That's fine, I don't mind offending over-sensitive people. My comment had no intent of malice in it. I am myself an undiagnosed yet obviously neurodivergent-to-a-certain-degree person myself. I've spent years describing my love for Haskell to anyone that would listen, evangelizing it as a side effect. Many people have taken my advice, so I had the chance to observe their Haskell journey. I also listened to many people talk about their experience teaching/evangelizing Haskell to others.
Anyway, my personal conclusion based on all that is Haskell simply doesn't work for many people (and not due to a lack of skill or intelligence) and yet it is love at first sight for others. It simply resonates with a certain kind of brain.
Please reconsider repeating this meme; it's one of the most odious in the Haskell community.
For those not familiar, many Haskellers believe that there are two types of people and that only one of those types is capable of writing statically-typed, correct, maintainable code. This is not a joke or exaggeration and it's one of the main reasons that I left the Haskell community.
I don't understand how such a simple statement can be so controversial. Haskell is an outlier among practical programming languages in many of the tradeoffs it makes, so in the end it ends up sacrificing so much in order to maximise compositionality and local reasoning. For example, Haskell chooses discriminated unions instead of flat union types, resulting in more modular code at the cost of less convenience. There are countless ways in which Haskell makes tradeoffs like this and the end result is that if you have a compatible set of preferences with it, you feel at home, but if not, then you constantly fight it without any perceived benefit over more mainstream languages. The same argument can be made for any language, but Haskell is different enough that your experience with it is likely to be dominated by these fundamental design choices, while you're more likely to hear someone say "I like Java more as a language, but I prefer Go due to the compile times".
And yes, I do believe it's connected to how your brain operates underneath. You can't convince me that the desire/need to sacrifice so much in exchange for the quiet comfort of local reasoning isn't correlated with neurology.
For some, it's instantly the cosy home their autistic mind was craving their entire lives
Yes. I used to think that it "is quiet", which may or may not make sense to others who are prone to overload. It's quiet, so my thoughts are clear and easy to translate into code.
This might make sense to me and makes me wonder if this is the same feeling I get with Go. With both languages it can go horribly wrong, though in very different ways. With Go you lose track of state, and with Haskell, you abstract out too early and lose the concrete.
I think this is why I enjoyed scala so much - I realized that I’d nerd sniped myself. I suspect that this applies to basically all functional languages which is why they have hardcore fandoms and also fail to break through to the mainstream, because each language tickles different brains differently.
I would be interested in hearing the author's experience with Haskell's error messages. I found them to be considerably worse than other languages. Almost the complete opposite of Rust, where the Rust compiler surfaces the underlying issue and helps the user fix them, Haskell produces a sea of gibberish and calls it an error message.
One more recent endeavor that has at least improved the situation is Haskell's newish Error Index. The compiler now emit codes like [GHC-12345] that you can lookup to learn more about the type of error and what it could mean: https://errors.haskell.org
Yeah, I have a love/hate relationship with Haskell.
My main two beefs are 1) error messages:
<interactive>:4:1: error: [GHC-39999]
• Could not deduce ‘Num t0’
from the context: (Num t1, Num (t1 -> t2))
bound by the inferred type for ‘it’:
forall {t1} {t2}. (Num t1, Num (t1 -> t2)) => t2
at <interactive>:4:1-7
The type variable ‘t0’ is ambiguous
Potentially matching instances:
instance Num Integer -- Defined in ‘GHC.Num’
instance Num Double -- Defined in ‘GHC.Float’
...plus three others
...plus one instance involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the ambiguity check for the inferred type for ‘it’
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
When checking the inferred type
it :: forall {t1} {t2}. (Num t1, Num (t1 -> t2)) => t2
A cookie for whomever can guess what caused this error :D
Beef 2) is laziness. To foldr, foldl or foldl', that is the question! is not something you should be watching over constantly.
I have to admit I have not gone deep into Haskell. I'd like to say that it's my two beefs who prevent me from using more the language, but it's just... it's even more daunting to me than Rust. And while I love the ideas and conciseness, it doesn't bring to me the same level of benefits than Rust.
Yeah, some specialization of error messages to cases like this (a Num instance for functions? Probably not what the user meant) would be nice. Then again maybe someone sometimes does write that instance?
I don't think Haskell's error messages are particularly great, but I never understood how Rust got a reputation for excelling at this.
The Rust type checker gives up and crashes for me every once a month or so. There's no worse error message than an internal panic. The only recourse is to delete code until you find the magic line it didn't like. Usually I am doing something that is in fact broken, but they just like, didn't implement an error for that case? You can find open issues on GitHub for these that go unfixed for years and years.
I've never seen GHC crash. The error messages are not always phrased in beginner-friendly terms, but they tend to accurately describe the situation and cover the full set of error conditions possible under the type system.
They're messy, but they're always helpful if you take the time to get them. OP does vibecoding, that might help a lot with powering through the gibberish.
Anyone have any experience with using Lean in this style of program? I'm curious how much the experience is one of FP vs. the specifics of the language.
There’s less library support for “normal” programs and the LSP is somehow even more annoying than HLS. I’d almost recommend Agda first. Both do add to the programmer’s burden by giving the option to easily encode more complicated properties in the type system, so you might face action paralysis and never get anything done except N different encodings of your application’s context in types.