As with all known Haskell systems, GHC implements some extensions to the language. To use them, you'll need to give a -fglasgow-exts option.
Virtually all of the Glasgow extensions serve to give you access to the underlying facilities with which we implement Haskell. Thus, you can get at the Raw Iron, if you are willing to write some non-standard code at a more primitive level. You need not be “stuck” on performance because of the implementation costs of Haskell's “high-level” features—you can always code “under” them. In an extreme case, you can write all your time-critical code in C, and then just glue it together with Haskell!
Executive summary of our extensions:
You can get right down to the raw machine types and operations; included in this are “primitive arrays” (direct access to Big Wads of Bytes). Please see Section 6.1 and following.
GHC's type system supports extended type classes with multiple parameters. Please see Section 6.5.
GHC's type system supports explicit universal quantification in constructor fields and function arguments. This is useful for things like defining runST from the state-thread world. See Section 6.6.
Some or all of the type variables in a datatype declaration may be existentially quantified. More details in Section 6.7.
Scoped type variables enable the programmer to supply type signatures for some nested declarations, where this would not be legal in Haskell 98. Details in Section 6.9.
Just what it sounds like. We provide lots of rope that you can dangle around your neck. Please see Section 6.4.
Pragmas are special instructions to the compiler placed in the source file. The pragmas GHC supports are described in Section 6.10.
The programmer can specify rewrite rules as part of the source program (in a pragma). GHC applies these rewrite rules wherever it can. Details in Section 6.11.
Before you get too carried away working at the lowest level (e.g., sloshing MutableByteArray#s around your program), you may wish to check if there are system libraries that provide a “Haskellised veneer” over the features you want. See Chapter 8.
These types correspond to the “raw machine” types you would use in C: Int# (long int), Double# (double), Addr# (void *), etc. The primitive operations (PrimOps) on these types are what you might expect; e.g., (+#) is addition on Int#s, and is the machine-addition that we all know and love—usually one instruction.
There are some restrictions on the use of unboxed types, the main one being that you can't pass an unboxed value to a polymorphic function or store one in a polymorphic data type. This rules out things like [Int#] (i.e. lists of unboxed integers). The reason for this restriction is that polymorphic arguments and constructor fields are assumed to be pointers: if an unboxed integer is stored in one of these, the garbage collector would attempt to follow it, leading to unpredictable space leaks. Or a seq operation on the polymorphic component may attempt to dereference the pointer, with disastrous results. Even worse, the unboxed value might be larger than a pointer (Double# for instance).
Nevertheless, A numerically-intensive program using unboxed types can go a lot faster than its “standard” counterpart—we saw a threefold speedup on one example.
Please see Section 8.3 for the details of unboxed types and the operations on them.