How to learn language extensions

I didn’t say that extension was ‘easy’; merely easier than what’s to come. :wink:

“flaming trashcan” is being too harsh. Thank you for being pernickety on that ticket, but I don’t think that should put off a newbie.

Although GHC treats extensions as orthogonal AFAP, educationally we should present them as building on each other: first FlexibleContexts; then FlexibleInstances; then MPTCs; etc. (Which corresponds roughly to the order they arrived. FlexibleContexts before 1998; KindSignatures 2007.)

Switching on KindSignatures is definitely not newbie level. Switching that on without FlexibleInstances is downright pathological. I suppose the documentation should get it right, but that’s not justification for the-sky-is-falling.

1 Like

No, still here. I’m trying to synthesise the advice. Like I said, I hate guesswork, and quite a bit of the advice is essentially that. (My fallback position is to simply avoid extensions I can’t understand. My purpose in learning Haskell is quite limited: I have just finished teaching a second year uni course on functional programming and standard Haskell is quite sufficient for that. I’d like to go a bit deeper before I teach the course again.)

Some other things off the top of my head…

WIBNI there were “groups” of extensions that cohere around particular topics (e.g. type-level programming), are known to work well together, and are well documented as a group. Perhaps these things exist informally?

Also, WIBNI there was a way of marking a defunct extension as deprecated.

2 Likes

I’m relieved!

Boy have I got a deal for you!

GHC blesses a number of extensions, beyond Haskell 2010, to be suitable to turned on by default. These extensions are considered to be stable and conservative.

“conservative” means highly unlikely to become deprecated. Documentation/tutorials for them as a group (good point!) are beginning to appear. So I’d get your head round those as a starter.

Beyond the blessed GHC2021, there’s a group around TypeFamilies; there’s a group around FunctionalDependencies – which two groups are kinda ‘competing’ mindsets for achieving the same end. (Usually your installation will have made a strategic choice between them.)

But yes, if you (or more likely some legacy library) uses a now-deprecated extension, you’ll get a compiler warning. It’s not that GHC completely takes away some functionality; rather there’s a more-general/more-Haskelly/more powerful way to achieve the same end. The extensions already deprecated are pretty obscure – I wouldn’t expect anybody up to intermediate level would want them. More likely it’ll be from some ancient tutorial written before the new shiny thing. There’s just going in to GHC more formal support for a gradual/general purpose deprecation cycle.

3 Likes

Re: FlexibleContexts

I deserve the rebuke. Thanks.

There is already a “building on each other” mechanism, as many more-advanced extensions implicitly turn on other extensions. I’d have no issue if KindSignatures actually implied FlexibleContexts, but that’s not the case. The linked example compiles even if I add the NoFlexibleContexts language pragma.

Wow! 46 extensions in alphabetical order. I may need a brain upgrade first. But at least it seems these satisfy my criterion of being known to work (well) together.

I’d be interested in any documentation or recommended resources for those groups. Or is the GHC user guide the best resource?

The GHC proposal has a more structured overview: https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0380-ghc2021.rst

At the top there is a grouping of the included extensions, they used the groups: Syntax, Literals, Types, Type signatures, Class instances and declarations, and Deriving mechanism.

And further down it lists all extensions in order of preference of the committee.

4 Likes

Weeeell, how else could that be organised? (I’ll try to think myself back to when Haskell was all new to me.)

Compare those big, sprawling languages. (You know who I mean, but I was a COBOL and SQL programmer for two decades.)

There’s C with preprocessor/macros overlaid (in effect a different language in the same source file) with C++ overlaid as a superset (does that really work?) with Template C++ overlaid.

Java I don’t know well but it seems to be a verbose mess.

Haskell starts with a small, perfectly-formed language (H2010) delivered with a comprehensive Prelude and feature-rich libraries. That’s what hooked me to start with. Then you hit limitations, and you discover there’s an extension for that. And you’ll note Haskell is also a research language, so some extensions count as ‘experimental’/not mature; you can avoid those.

Can you use a disciplined subset of C/Java like that and have the compiler warn you you’re straying into dangerous territory? If you find a library/package that seems to fit your need, can you quiz it as to whether it’s using unfamiliar constructs? Or do you have to learn/know the language as a homogenous blob? Perhaps you already got a “brain upgrade” to work with those languages, but they took up so much brainpower they squeezed out the memory?

3 Likes

@AntC2 Yes, my comment was rather dismissive/ungrateful, which wasn’t my intention. The structure of Haskell is rather wonderful compared to the likes of C++ and, in my view the ultimate kitchen sink language, Scala. One of my favourite languages, Rust, with its editions has a similar approach to Haskell, but language features are essentially promoted into the core, which makes it less easy for beginners to learn the 1.0 language in retrospect.

I grew up with Java, so I learned most of the language in digestible stages, but that would have been much harder coming to the whole current language for the first time. I wonder if something similar has gone on with those well-versed in Haskell extensions: have they grown up with some favourite extensions and then gradually expanded their repertoire?

How else could it be organised? The GHC2021 proposal linked by @jaror was helpful in suggesting some groupings of extensions. The The GHC20XX process linked from there was also helpful in describing the rationale.

So I guess I just need to pick a group of extensions and learn them. Going back to the OP, FlexibleContexts is part of the “Class and instances declarations” grouping of GHC2021 but TypeFamilies doesn’t seem to be in GHC2021 at all. So that suggests I should prioritise FlexibleContexts and defer learning TypeFamilies for now.

What I think would be really helpful would be some introductory material for each grouping and I probably just need to put the work in to dig out relevant articles (e.g. Haskell: GHC2021 and language extensions in 2022).

1 Like

Yes, well observed: that’s exactly how I ‘grew up’.

Nor FunctionalDependencies, and yet MultiParamTypeClasses is (even though it’s not needed for the Prelude nor most core libraries). If you want MPTCs, you pretty much gotta add either TFs or FunDeps, and they’ll probably want UndecidableInstances. IMO including MPTCs leaves you up a creek without a paddle – or perhaps I mean at a fork in the creek. So I’d defer MPTCs to the end of your learning schedule.

1 Like

I think your efforts would be better spent starting the journey of learning about type level programming. Not all GHC extensions are equal in complexity, some can be learned in a matter of minutes, and some can take a very long time. But I guarantee that if you open a random Haskell file (like the one you mentioned in your original post) the majority of the extensions there which you don’t recognize are related to type level programming, or at least are being used specifically to enable it. Not all of them of course, but the majority of them. And moreover, if you separate out the ones which take five minutes to learn (e.g. LambdaCase) and those that don’t, the type level programming ones will be in the latter. It would be a better use of your time and energy to start broaching that subject than to sit down and read the documentation of individual language extensions on their own.

Here’s an obnoxious Socratic question that may help explain what I mean: imagine the person who wrote that file you were looking at used GHC2021 and so FlexibleContexts was on by default and didn’t appear at the top of the file. Would you care in the absolute slightest about learning that extension, and would you have felt any impetuous to look it up? If it was the way Haskell typeclasses worked from the very beginning, would you even think to distinguish someone ‘using’ it in their code vs someone who wasn’t? Of course you wouldn’t, because it would require understanding what that extension does to even recognize it in the code. The only reason you chose that specific extension to want to learn is because you saw its name in the file, not because you saw some code and didn’t understand what it did. If it wasn’t an extension, it would just be some other aspect of Haskell as a language which you hadn’t met or learned about yet and so the answer to the question of “how should I learn about it?” would be to learn more about the type class system and type level programming in general. If you didn’t understand the code in the first place, then FlexibleContexts being an extension would have been a red herring.

I think it makes way more sense and will be better in the long run to set your goal to be ‘learn type level programming’ than to be ‘work through a list of important extensions’, even if that list of extensions is exactly the same list you’d end up learning if your goal was the former. Plus, if your goal is to help contribute to projects like you mentioned in your first post, then this is the cluster of extensions which will pay the most dividends because they’re an innate part of Haskell programming as it’s practiced in the real world today despite being added late.

9 Likes

I think this is an important observation. Haskell tries to be a “good citizen” by being precise about deviations from the published standard but in some ways it does more harm than good because seeing a long list of LANGUAGE at the top of a file worries people. It shouldn’t!

9 Likes

Yea I agree with those last couple of statements. Ive never really worried about language extensions, I just use the hackage docs to guide me on how to use libraries and if HLS complains I need an extension, then I enable it. I feel like I’ve gotten atleast a decent handle on things that way.

In general, I’ve noticed I’m much more successful in Haskell when I focus on “how to use things/do stuff” rather than “how they work”.

2 Likes

I think focussing on type-level programming is probably good advice. Not sure where to start, but I dare say an internet search would yield some interesting possibilities.

I like your Socratic question. I guess I’m comfortable not knowing something without knowing I don’t know it, but knowing that I don’t know something just makes me want to find out about it. In the past, I’ve always found it more satisfying to investigate things I don’t know, rather than just “winging it”.

3 Likes

Two thing I’m really happy about in the Haskell ecosystem is that a lot of the recent introductions to the language do end up talking about basic type level ideas (thus, hopefully, normalizing their existence), and that we now have a good collection of books aimed at intermediate Haskell writers to help them move to the next steps. Rebecca Skinner’s new book Effective Haskell is fantastic and it ends with a chapter which introduces the general idea of why you would want to use things like TypeFamilies and GADTs with really concrete examples. She also has a blog post, along with a talk version, which I think similarly does a great job of explaining what the concepts involved are and why you would want to use them. Alejandro Serrano Mena’s book Practical Haskell has a section which compares the FunctionalDependencies and TypeFamilies approaches and shows how they both solve the kinds of problems we want to use type level programming for. Matt Parsons’s new book Production Haskell has a great section introducing the basics of type level programming, and an older version of it is also available as a blog post. This was also mentioned earlier, but I’ll say again that Vitaly Bragilevsky’s Haskell In Depth has a great section on type level programming. Sandy Maguire wrote a book entirely about type level programming as well. But with all of that said, and as you mentioned, just googling the term will come up with tons of other results, whether they be blog posts, tutorials, talks, or other books.

I completely agree, and I didn’t mean to suggest that you shouldn’t be curious about these things, of course! What I meant by those series of questions was just to point out that the fact that those are language extensions vs something in the language proper which you didn’t understand is an arbitrary distinction insofar as the technique you should use to learn about them is concerned. I’m repeating myself now so I’ll stop after this, but some extensions are really just simple syntax changes which are easy to explain and grasp, but others are more complicated and serve the purpose of changing the techniques you use to program. I think it’s better to learn the latter kinds of extensions in the context of learning those techniques as opposed to thinking purely in terms of “I need to learn the list of extensions that appears in this file I’m trying to understand.”

5 Likes

(I’m not in the O.P.'s position of “relatively new to Haskell”.)

FlexibleContexts I wouldn’t be worried about, no. But there’s other gremlins included in GHC2021 that I’ve been bitten by, so I’d feel impetus to look see if the code’s using any of those.

FlexibleContexts does seem a simple syntax change. FlexibleInstances seems at first sight an equally simple syntax change. But it can give OverlappingInstances: not at all simple, especially when combined with MultiParamTypeClasses.

I finally found an idle moment to try this. Guess what? With both the FlexibleContexts and TypeFamilies extension pragmas commented out, the code compiled and all the tests passed! So your advice turned out to be better than I expected, thanks.

5 Likes

What an amazing conclusion to the thread! :grinning_face_with_smiling_eyes:

3 Likes

Meanwhile using beam in freeze:

default-extensions: 
- RecordWildCards
- ViewPatterns
- BlockArguments
- OverloadedStrings
- LambdaCase
- DuplicateRecordFields
- NamedFieldPuns
- FlexibleInstances
- FlexibleContexts
- TypeFamilies 
- MultiParamTypeClasses
- UndecidableInstances
- DeriveGeneric 
- DeriveAnyClass
- StandaloneDeriving
- ImpredicativeTypes
- TypeSynonymInstances

The first five are preference, the sixth choice, and the rest the compiler told me to.

Interestingly in this project when updating ghc / resolver: a syntax that worked previously to reference one of the duplicate record fields with a type i.e.

data A = A { a :: T }
data B = B { a :: T}
getA :: A -> T
getA = a
-- getA (A a) = a 
-- (\(A a)->a) -- or inline  

Updating caused this to not work anymore.

2 Likes

I can really recommend using OverloadedRecordDot if you have duplicate field names. It allows you to reference fields like myRecord.field.

It has simplified our code ase significantly as we used a combination of custom getters, field selectors and lenses in the past.

5 Likes

there is the obvious fact that some extensions are harmless, while others aren’t.

AllowAmbiguousTypes is just the best example where GHC misleads the programmer. I think one can also be mislead into the use of GADTs and RankNTypes.

And that’s the actual problem: someone who sets out to learn Haskell obviously can’t distinguish the straightforward extensions from the less straightforward ones.

GADTs and TypeFamilies require quite a bit of reading and can completely transform your use of Haskell. LambdaCase and BlockArguments are kind of stylistic features.

For a beginner, any language extension is a strange encounter and you rightfully feel like Alice in Wonderland. In those situations, having someone sitting next to you who just tells you “You don’t want to do this” or “This one’s fine” is invaluable.

I remember how I tried to follow GHC errors to fix my code and ended up with RankNTypes, still unable to get my code to compile. An experienced programmer just told me: You probably don’t want to do this. So I stopped wasting more time trying around and got back to actual programming.

Maybe there is still demand for a GHC language extensions tutorial (apologies for my ignorance, if this exists and has been linked already)

3 Likes