When programming in Haskell, you do not read the documentation — you trust the compiler and rush forward with your eyes fixed on the goal, like an anime character.
bad news
Trying to to understand what extensions do is impossible, because trying to understand how GHC works is impossible to begin with. For example, there is no way you can emulate GHC’s type inference with pen and paper — it is too complicated, and changes in subtle ways over time. I asked about it some time ago because I wanted to do fancy type level programming and I could not understand why something is not working. Here is what @nomeata told me:
Ignat Is there a comprehensive description of the type inference rules used by the current GHC ?
Joachim No, unfortunately not. It’s spread over a bunch of papers, and never complete. I discussed this with Richard [I guess @rae Eisenberg] recently, and yet, would be good to have something like that!
Some extensions remove restrictions that turned out to be needless — say FlexibleContexts
. Trying to understand them means digging into the motivation of the people who were working on GHC 20 or 30 years ago — a job for a historian.
Other extensions add features that are often half baked and experimental. For example:
{-# language FunctionalDependencies, TypeFamilyDependencies, UnicodeSyntax #-}
class X a b c | a b → c, c b → a
type family F a b = c | c b → a
These two type level spells should do the same. But the latter will not work. The article that introduced type family dependencies — Injective Type Families for Haskell by Jan Stolarek, Simon @simonpj and Richard @rae — only has this to say:
The check that the injectivity annotation is sound would be an extension of Definitions 2 and 8, and the improvement rule would mimic the one for functional dependencies. However, this remains as future work: we have not yet extended the metatheory or implementation to accommodate it.
— Injective Type Families for Haskell, section 7.2.
Maybe in the future we shall have an extension FlexibleTypeFamilyDependencies
that would amend this. But there is a lot of issues like this with the newer features of Haskell, when something that should be working in principle turns out not to work in practice.
It is impossible to understand how GHC executes your program, as well. Sometimes it will let float something and turn your logarithmic complexity into polynomial, or something such. I thought I understood after I read Simon @simonpj 's book Implementing Functional Languages, but Lennart @augustss was quick to point out that my understanding is naïve and incomplete.
good news
However, even though I understand Haskell so poorly, I am unafraid to tackle any code base.
In some other languages, like say C or JavaScript, documentation is the only way to understand how something should be working, and there are subtle issues at every step of the way that you must keep in mind. Constant vigilance. The habit of thoroughly reading the relevant documentation would have served me well — when I try to write something in C it either segmentation faults or at best silently does nothing.
But Haskell is not like this. You can trust it. If it compiles, it works. You only need to watch out for ill-founded recursion and be aware of dangers associated with asynchronous IO. Other than that, Haskell will take care of you.
The hard task is to get your fancily typed program to compile. But this is a kind of task you should work on when you do already have a fancily typed program on your hands that does not compile. There are a lot of people here (say me) who will be eager to help you out. Beyond the grasp of a mere human Haskell may be, but as a hive mind we can always figure it out. Bring the code, and we shall fix it.