One of the most intimidating aspects of being new to Haskell are the overwhelming number of language extensions GHC Haskell has to offer. Some are well documented, others are sparsely documented; some seem to appear in so much “real world” Haskell code; some are spoken of in hushed tones as being unsafe and unwise while others are … maybe safe? It can be difficult terrain to navigate.
The first thing to understand is that most programs can be written in vanilla Haskell2010 or even Haskell98 with no extensions enabled. The extensions do not change the languages or its semantics. They are compiler extensions and change the way the compiler does its job, the way it reads the code. Hence, if you are using a compiler other than GHC (or are using a very old version of GHC), you may not have access to any of these extensions or only to some subset.
This course covers the ones we think are most likely to be useful and helpful to the Haskell beginner or early intermediate as well as a few having to do with issues that crop up whether you’re ready for them or not. Each has a link to a longer article about it, describing it in more detail.
What is a language extension?
Language extensions can be enabled using a pragma called LANGUAGE
. A pragmaApparently this use of the term ‘pragma’ originates with ALGOL 68, although it was pragmat then, and is derived from the word pragmatic. is an instruction to the compiler about how to process the input code. They can, for instance, be used to specify certain optimization rules so the compiler handles the code a bit differently from normal.
Pragmas do not generally affect the semantics of your program, but can, for example, allow variations in syntax or alter the way the program is evaluated. The word pragma derives from the same sense of pragmatic that linguists use to talk about the way context can change the way utterances are understood, without affecting the basic semantics of the utterance.
GHC supports several pragmas. They are all written inside special comment syntax ({-# ... #-}
) with the name of the pragma in all caps followed by any information specific to that type of pragma, such as the name of a language extension or compiler flag:
{-# LANGUAGE <NameOfExtension> #-}
{-# OPTIONS_GHC <compiler_flag> #-}
{-# INLINE <function_name> #-}
{- This is a regular block comment, not a pragma -}
The LANGUAGE
pragma is used to enable extensions to the Haskell language. Sometimes the changed syntax they enable is merely convenient, but many of them allow you to write Haskell very differently from the basic language as specified in the Haskell Report.
LANGUAGE
is a file-header pragmaSome pragmas, such as INLINE
, are not written at the top of a file and their scope is limited. See the documentation for more information., so language extensions are listed at the very top of the file, above the module name, and the entire file will be compiled in that context – although it is ok to turn on a language extension and then never use the syntax it enables within that file. This isn’t really recommended and can have (usually small) effects on compile time but you won’t break anything by turning some extensions on and then not using them (or deleting the code that did need them but forgetting to delete the pragma).
Every language extensionA list of all the language extensions your version of GHC supports can be obtained by invoking ghc --supported-extensions
. can also be turned into a command-line flag by prefixing it with -X
; for example -XForeignFunctionInterface
. So, for example, you can turn that on when you invoke GHC on the command line to compile your program. You can also use that to turn them on in GHCi, using GHCi’s :set
command, e.g., :set -XTypeApplications
. The :set
command can also go in your GHCi configuration, to enable an extension by default whenever you start GHCi. You can also turn language extensions onIf you are using hpack, you can turn on language extensions in your package.yaml
file. for an entire project using the default-extensions
field in your .cabal
file.
Implications
Some language extensions build on others: for example, ExplicitForAll
introduces the forall
keyword, and ScopedTypeVariables
expands upon the significance of forall
. Since scoped type variables would not be useful without forall
, GHC enables ExplicitForAll
automatically whenever you turn on ScopedTypeVariables
. In this course, we depict this relationship as follows:
We read this as “ScopedTypeVariables
implies ExplicitForAll
”.
Recommended first extensions
People often ask where they should start with learning extensions. The list of all available extensions is overwhelmingly large, and it’s difficult to gauge how useful and safe they are when you don’t have much experience (and sometimes, even if you do). We’ve compiled this short list of the extensions we recommend as your first set to learn and use. This page only gives a short description and a reason why we recommend it. Please see the linked longer articles for more information about each.
Useful for learning
NoImplicitPrelude
–Prelude
is automatically imported unless this extension is enabled. Use this when you do not want to import the standardPrelude
. Turning it off is highly recommended for learning, as it will enable you to reimplement basic functions, types, and typeclasses, without name clashes.TypeApplications
– Visible type applications allow you to specify type arguments for polymorphic functions. You can use this right in GHCi if you like, to see the type signature of a polymorphic function once it’s been applied to concrete types.InstanceSigs
– Allows type signatures ininstance
declarations. This can be useful as you’re learning to write typeclass instances, to keep the type signatures of the functions you’re implementing in sight.ScopedTypeVariables
– This extension allows you to give an explicit type signature to any subterm of a program. This can be useful to see (and encourage type inference) of intermediate types in complex functions.PartialTypeSignatures
– If you are familiar with typed holes, this is similar. This allows for wildcard placeholders in type signatures, allowing you to provide explicit, but partial, type annotations and let the compiler help you figure out how to fill them in. This extension goes along with theNamedWildCards
extension.GADTSyntax
– This extension allows you to write datatypes that syntactically look like generalized algebraic datatypes but act like regular datatypes. Using this syntax, you write the type for each data constructor. That makes the “functionness” of each constructor explicit and readily apparent, which is great for learning.
Easy, helpful, safe
LambdaCase
– This extension is completely unnecessary but turns on a convenient shorthand for writingcase
expressions.GeneralizedNewtypeDeriving
– While we recommend that you learn to writeinstance
declarations by hand, this extension is useful to know – and useful to use if you are the point in your learning where you are usingnewtype
s but not ready to write instances for many typeclasses. This enables you to derive any typeclass instance for anewtype
that its underlying type has.NumericUnderscores
– This extension allows you to use underscores in numeric literals as placeholders (the way you might use commas or dots in large numbers, to separate groups of digits).
Good to know
OverloadedStrings
– This extension is so commonly used that you will almost certainly encounter it as you’re learning, so it’s good to understand and be familiar with.NoMonomorphismRestriction
– The monomorphism restriction is poorly understood by many Haskellers, but it can cause what seems like unpredictable evaluation behavior. We recommend starting with this article and experimenting with the extension and seeing how behaviors change.QuasiQuotes
– This is one of our more controversial recommendations, but it’s useful when you need to handle multi-line strings, which seems to be a common enough problem that it’s worth at least familiarizing yourself with it.