... | @@ -22,7 +22,7 @@ The general rule is to stick to the same coding style as is already used in the |
... | @@ -22,7 +22,7 @@ The general rule is to stick to the same coding style as is already used in the |
|
**It's much better to write code that is transparent than to write code that is short.**
|
|
**It's much better to write code that is transparent than to write code that is short.**
|
|
|
|
|
|
For example, compare
|
|
For example, compare
|
|
```
|
|
```haskell
|
|
f1, f2 :: TcM a -> TcM (a, [b])
|
|
f1, f2 :: TcM a -> TcM (a, [b])
|
|
f1 thing_inside = flip (,) [] <$> thing_inside
|
|
f1 thing_inside = flip (,) [] <$> thing_inside
|
|
|
|
|
... | @@ -41,7 +41,7 @@ As a rule of thumb, when writing monomorphic code, prefer using a monomorphic fu |
... | @@ -41,7 +41,7 @@ As a rule of thumb, when writing monomorphic code, prefer using a monomorphic fu |
|
Even when one has genuinely polymorphic code, it can be better to write out the code longhand than to reuse a generic abstraction (not always, of course). Sometimes it's better to duplicate some similar code than to try to construct an elaborate generalisation with only two instances. Remember: other people have to be able to quickly understand what you've done, and overuse of abstractions just serves to obscure the *really* tricky stuff, and there's no shortage of that in GHC.
|
|
Even when one has genuinely polymorphic code, it can be better to write out the code longhand than to reuse a generic abstraction (not always, of course). Sometimes it's better to duplicate some similar code than to try to construct an elaborate generalisation with only two instances. Remember: other people have to be able to quickly understand what you've done, and overuse of abstractions just serves to obscure the *really* tricky stuff, and there's no shortage of that in GHC.
|
|
|
|
|
|
Here's a real-life example (!9037). Here are two functions that do the same thing:
|
|
Here's a real-life example (!9037). Here are two functions that do the same thing:
|
|
```
|
|
```haskell
|
|
f1, f2, f3 :: [a] -> [a] -> [[a]]
|
|
f1, f2, f3 :: [a] -> [a] -> [[a]]
|
|
f1 as = sequenceA $ (:) <$> as
|
|
f1 as = sequenceA $ (:) <$> as
|
|
f2 as bs = [a:bs | a <- as]
|
|
f2 as bs = [a:bs | a <- as]
|
... | @@ -52,11 +52,11 @@ For GHC we much prefer `f2` or `f3`. The former uses a list comprehension; the |
... | @@ -52,11 +52,11 @@ For GHC we much prefer `f2` or `f3`. The former uses a list comprehension; the |
|
Understanding `f1` is quite a bit more demanding. To be clear, **there is nothing wrong with `f1`** for people who have `sequenceA` and friends paged into their mental cache. But for those who don't, here's the journey of at least one reader.
|
|
Understanding `f1` is quite a bit more demanding. To be clear, **there is nothing wrong with `f1`** for people who have `sequenceA` and friends paged into their mental cache. But for those who don't, here's the journey of at least one reader.
|
|
|
|
|
|
Let's start with type checking. For `f1` we must look up the type of `sequenceA`:
|
|
Let's start with type checking. For `f1` we must look up the type of `sequenceA`:
|
|
```
|
|
```haskell
|
|
sequenceA :: forall t f b. (Traversable t, Applicative f) => t (f b) -> f (t b)
|
|
sequenceA :: forall t f b. (Traversable t, Applicative f) => t (f b) -> f (t b)
|
|
```
|
|
```
|
|
Now we must figure out how `t` and `f` are instantiated. Let's figure out the type of `(:) <$> as`. Well, `as :: [a]`, so `<$> :: Functor g => (p->q) -> g p -> g q` is being used with `g=[]` and `q = [a] -> [a]`. So we have
|
|
Now we must figure out how `t` and `f` are instantiated. Let's figure out the type of `(:) <$> as`. Well, `as :: [a]`, so `<$> :: Functor g => (p->q) -> g p -> g q` is being used with `g=[]` and `q = [a] -> [a]`. So we have
|
|
```
|
|
```haskell
|
|
(:) <$> as :: [ [a] -> [a] ]
|
|
(:) <$> as :: [ [a] -> [a] ]
|
|
```
|
|
```
|
|
Now that type must match the argument of `sequenceA`, which has type `t (f b)`,
|
|
Now that type must match the argument of `sequenceA`, which has type `t (f b)`,
|
... | @@ -65,19 +65,19 @@ so we must have `t = []` and `f = (->) [a]` and `b = [a]`. |
... | @@ -65,19 +65,19 @@ so we must have `t = []` and `f = (->) [a]` and `b = [a]`. |
|
Right, so the result of `sequenceA` has type `f (t b)` which is `[a] -> [[a]]`. That matches the type signature for `f1`, so the program is type correct.
|
|
Right, so the result of `sequenceA` has type `f (t b)` which is `[a] -> [[a]]`. That matches the type signature for `f1`, so the program is type correct.
|
|
|
|
|
|
Now we need to work out what `sequenceA @[] @((->) a)` actually *does*. It's a method of the `Traversable` class, so we need the `Traversable []` instance. Instances aren't all that easy to find (you can't grep for them the way you can grep for a function name), but it's in `Data.Traversable`:
|
|
Now we need to work out what `sequenceA @[] @((->) a)` actually *does*. It's a method of the `Traversable` class, so we need the `Traversable []` instance. Instances aren't all that easy to find (you can't grep for them the way you can grep for a function name), but it's in `Data.Traversable`:
|
|
```
|
|
```haskell
|
|
instance Traversable [] where
|
|
instance Traversable [] where
|
|
traverse f = List.foldr cons_f (pure [])
|
|
traverse f = List.foldr cons_f (pure [])
|
|
where cons_f x ys = liftA2 (:) (f x) ys
|
|
where cons_f x ys = liftA2 (:) (f x) ys
|
|
```
|
|
```
|
|
Oh. No actual definition of `sequenceA`. Let's look at the default method:
|
|
Oh. No actual definition of `sequenceA`. Let's look at the default method:
|
|
```
|
|
```haskell
|
|
class (Functor t, Foldable t) => Traversable t where
|
|
class (Functor t, Foldable t) => Traversable t where
|
|
sequenceA :: Applicative f => t (f a) -> f (t a)
|
|
sequenceA :: Applicative f => t (f a) -> f (t a)
|
|
sequenceA = traverse id
|
|
sequenceA = traverse id
|
|
```
|
|
```
|
|
OK so in our call of `sequenceA` it really means
|
|
OK so in our call of `sequenceA` it really means
|
|
```
|
|
```haskell
|
|
sequenceA @[] @((->) a)
|
|
sequenceA @[] @((->) a)
|
|
= traverse @[] @((->) a) id
|
|
= traverse @[] @((->) a) id
|
|
= List.foldr cons_f (pure [])
|
|
= List.foldr cons_f (pure [])
|
... | @@ -90,7 +90,7 @@ None of this is easy. We prefer `f2` or `f3`. **General rule: use monomorphic |
... | @@ -90,7 +90,7 @@ None of this is easy. We prefer `f2` or `f3`. **General rule: use monomorphic |
|
if you can**.
|
|
if you can**.
|
|
|
|
|
|
Another simpler example. Suppse `f :: t2 -> t3` and `xs :: [(t1,t2)]` Instead of
|
|
Another simpler example. Suppse `f :: t2 -> t3` and `xs :: [(t1,t2)]` Instead of
|
|
```
|
|
```haskell
|
|
fmap f <$> xs -- Two uses of fmap, in different notation, one
|
|
fmap f <$> xs -- Two uses of fmap, in different notation, one
|
|
-- of which is over a partial application of (,)
|
|
-- of which is over a partial application of (,)
|
|
```
|
|
```
|
... | @@ -99,21 +99,21 @@ write |
... | @@ -99,21 +99,21 @@ write |
|
mapSnd f xs
|
|
mapSnd f xs
|
|
```
|
|
```
|
|
where `mapSnd :: (b->c) -> [(a,b)] -> [(a,c)]` is defined in `GHC.Utils.Misc`. Actually, even there `mapSnd` generalises over the list part:
|
|
where `mapSnd :: (b->c) -> [(a,b)] -> [(a,c)]` is defined in `GHC.Utils.Misc`. Actually, even there `mapSnd` generalises over the list part:
|
|
```
|
|
```haskell
|
|
mapSnd :: Functor f => (b->c) -> f (a,b) -> f (a,c)
|
|
mapSnd :: Functor f => (b->c) -> f (a,b) -> f (a,c)
|
|
```
|
|
```
|
|
|
|
|
|
### 1.4 INLINE and SPECIALISE pragmas
|
|
### 1.4 INLINE and SPECIALISE pragmas
|
|
|
|
|
|
If inlining or specialisation is important for performance, use pragmas to say so. Consider
|
|
If inlining or specialisation is important for performance, use pragmas to say so. Consider
|
|
```
|
|
```haskell
|
|
f = ...big....
|
|
f = ...big....
|
|
|
|
|
|
g :: Num a => Int -> a
|
|
g :: Num a => Int -> a
|
|
g x = if (f x) then 1 else 2
|
|
g x = if (f x) then 1 else 2
|
|
```
|
|
```
|
|
It is tempting to think that "`g` is small, so GHC's inlining heuristics will inline `g` at every call site, so there will be no type-class overhead". But this reasoning is fragile. For example, if this is the only call to `f`, GHC will inline it to give
|
|
It is tempting to think that "`g` is small, so GHC's inlining heuristics will inline `g` at every call site, so there will be no type-class overhead". But this reasoning is fragile. For example, if this is the only call to `f`, GHC will inline it to give
|
|
```
|
|
```haskell
|
|
g :: Num a => Int -> a
|
|
g :: Num a => Int -> a
|
|
g x = if (...big...) then 1 else 2
|
|
g x = if (...big...) then 1 else 2
|
|
```
|
|
```
|
... | @@ -127,7 +127,8 @@ Do not rely on heuristics. And add a comment to explain the importance of the p |
... | @@ -127,7 +127,8 @@ Do not rely on heuristics. And add a comment to explain the importance of the p |
|
|
|
|
|
The Haskell source code in GHC is indented using spaces only. The usual indentation level is 2 spaces, but some modules follow a different convention, which you should try to stick to.
|
|
The Haskell source code in GHC is indented using spaces only. The usual indentation level is 2 spaces, but some modules follow a different convention, which you should try to stick to.
|
|
|
|
|
|
Other, non-Haskell-code is indented with a mixture of tabs and spaces, and is standardised on a tabstop of 8.
|
|
Other, non-Haskell-code is indented with a mixture of tabs and spaces, and is standardised on a tab-stop of 8.
|
|
|
|
|
|
|
|
|
|
## 2. Using Notes
|
|
## 2. Using Notes
|
|
|
|
|
... | @@ -152,7 +153,7 @@ Thus motivated, we use the following very simple convention: |
... | @@ -152,7 +153,7 @@ Thus motivated, we use the following very simple convention: |
|
```
|
|
```
|
|
|
|
|
|
* At the point where the comment is relevant, we add a short comment referring to the Note:
|
|
* At the point where the comment is relevant, we add a short comment referring to the Note:
|
|
```
|
|
```haskell
|
|
data Type
|
|
data Type
|
|
= FunTy Type Type -- See Note [Equality-constrained types]
|
|
= FunTy Type Type -- See Note [Equality-constrained types]
|
|
|
|
|
... | @@ -216,8 +217,8 @@ The main content of a commit message should *not* be an explanation of the curre |
... | @@ -216,8 +217,8 @@ The main content of a commit message should *not* be an explanation of the curre |
|
|
|
|
|
In short, commit messages describe *changes*, whereas comments explain the code *as it now is*.
|
|
In short, commit messages describe *changes*, whereas comments explain the code *as it now is*.
|
|
|
|
|
|
## 4. Warnings
|
|
|
|
|
|
|
|
|
|
## 4. Warnings
|
|
|
|
|
|
We are aiming to make the GHC code warning-free, for all warnings turned on by
|
|
We are aiming to make the GHC code warning-free, for all warnings turned on by
|
|
|
|
|
... | @@ -225,22 +226,20 @@ We are aiming to make the GHC code warning-free, for all warnings turned on by |
... | @@ -225,22 +226,20 @@ We are aiming to make the GHC code warning-free, for all warnings turned on by |
|
-Wall
|
|
-Wall
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
The build automatically sets these flags for all source files (see `mk/warnings.mk`).
|
|
The build automatically sets these flags for all source files (see `mk/warnings.mk`).
|
|
|
|
|
|
|
|
|
|
The [validate script](testing-patches), which is used to test the build before commiting, additionally sets the `-Werror` flag, so that the code **must** be warning-free to pass validation. The `-Werror` flag is not set during normal builds, so warnings will be printed but won't halt the build.
|
|
The [validate script](testing-patches), which is used to test the build before commiting, additionally sets the `-Werror` flag, so that the code **must** be warning-free to pass validation. The `-Werror` flag is not set during normal builds, so warnings will be printed but won't halt the build.
|
|
|
|
|
|
|
|
|
|
Currently we are some way from our goal, so some modules have a
|
|
Currently we are some way from our goal, so some modules have a
|
|
|
|
|
|
```haskell
|
|
```haskell
|
|
{-# OPTIONS_GHC -fno-warn-... #-}
|
|
{-# OPTIONS_GHC -fno-warn-... #-}
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
pragma; you are encouraged to remove this pragma and fix any warnings when working on a module.
|
|
pragma; you are encouraged to remove this pragma and fix any warnings when working on a module.
|
|
|
|
|
|
|
|
|
|
## 5. Exports and Imports
|
|
## 5. Exports and Imports
|
|
|
|
|
|
### Exports
|
|
### Exports
|
... | @@ -252,14 +251,12 @@ module Foo ( |
... | @@ -252,14 +251,12 @@ module Foo ( |
|
) where
|
|
) where
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
We usually (99% of the time) include an export list. The only exceptions are perhaps where the export list would list absolutely everything in the module, and even then sometimes we do it anyway.
|
|
We usually (99% of the time) include an export list. The only exceptions are perhaps where the export list would list absolutely everything in the module, and even then sometimes we do it anyway.
|
|
|
|
|
|
|
|
|
|
It's helpful to give type signatures inside comments in the export list, but hard to keep them consistent, so we don't always do that.
|
|
It's helpful to give type signatures inside comments in the export list, but hard to keep them consistent, so we don't always do that.
|
|
|
|
|
|
### Imports
|
|
|
|
|
|
|
|
|
|
### Imports
|
|
|
|
|
|
List imports in the following order:
|
|
List imports in the following order:
|
|
|
|
|
... | @@ -285,57 +282,23 @@ List imports in the following order: |
... | @@ -285,57 +282,23 @@ List imports in the following order: |
|
import Data.Maybe
|
|
import Data.Maybe
|
|
```
|
|
```
|
|
|
|
|
|
|
|
|
|
Import library modules from the [boot packages](commentary/libraries) only (boot packages are those packages in the file [packages](https://gitlab.haskell.org/ghc/ghc/blob/master/packages) that have a '-' in the "tag" column). Use `#defines `in `HsVersions.h` when the modules names differ between versions of GHC. For code inside `#ifdef GHCI`, don't worry about GHC versioning issues, because this code is only ever compiled by the this very version of GHC.
|
|
Import library modules from the [boot packages](commentary/libraries) only (boot packages are those packages in the file [packages](https://gitlab.haskell.org/ghc/ghc/blob/master/packages) that have a '-' in the "tag" column). Use `#defines `in `HsVersions.h` when the modules names differ between versions of GHC. For code inside `#ifdef GHCI`, don't worry about GHC versioning issues, because this code is only ever compiled by the this very version of GHC.
|
|
|
|
|
|
In general, we recommend **not using explicit import lists**. There are several reasons for this:
|
|
In general, we recommend using explicit import lists only when they convey useful information without imposing excessive an maintenance burden. That is, when you are using only a few declarations from an imported module an explicit import list can be useful for future readers. However, if you use a significant fraction of a module's exports then the export list is likely to incur more of a maintenance and readability burden than benefit.
|
|
|
|
|
|
- They slow down development: almost every change is accompanied by an import list change.
|
|
|
|
|
|
|
|
- They cause spurious conflicts between developers.
|
|
|
|
|
|
|
|
- They lead to useless warnings about unused imports, and time wasted trying to
|
|
In general we tend not to use `qualified` imports very often since we try choose names to avoid clashes (e.g. the map function for `OrdList` is named `mapOL` instead of merely `map`).
|
|
keep the import declarations "minimal".
|
|
|
|
|
|
|
|
- GHC's warnings are useful for detecting unnecessary imports: see `-fwarn-unused-imports`.
|
|
One area where we do generally require export and import lists is in `hs-boot` files in order to clarify the reason for the boot file. This can help identify refactorings that would eliminate `hs-boot` files.
|
|
If the module can be compiled multiple ways (eg. GHCI vs. non-GHCI), make sure the imports
|
|
|
|
are properly `#ifdefed`, so as to avoid spurious unused import warnings.
|
|
|
|
|
|
|
|
- TAGS is a good way to find out where an identifier is defined (use [hasktags](https://github.com/MarcWeber/hasktags) or a similar program in `ghc/compiler` to generate TAGS file,
|
|
|
|
and hit `M-.` in Emacs; see [here](emacs#using-tags-to-quickly-locate-definitions-in-a-project) for details).
|
|
|
|
|
|
|
|
There are of course exceptions:
|
|
|
|
|
|
|
|
- An explicit import needed to avoid a name clash.
|
|
|
|
- Explicit `{- SOURCE -#}` import lists help keep track of why an `hs-boot` file is necessary.
|
|
|
|
This can help identify refactorings that would eliminate `hs-boot` files.
|
|
|
|
- When importing a large module which exports several logically separate components, an explicit
|
|
|
|
import can signpost which part one needs.
|
|
|
|
|
|
|
|
In short: only use an explicit import list when it would communicate useful information to other developers.
|
|
|
|
|
|
|
|
## 6. Compiler versions and language extensions
|
|
## 6. Compiler versions and language extensions
|
|
|
|
|
|
|
|
GHC must be compilable and validate by the previous two major GHC releases, and itself. It isn't necessary for it to be compileable by every intermediate development version.
|
|
|
|
|
|
GHC must be compilable and validate by the previous two major GHC releases, and itself. It isn't necessary for it to be compilable by every intermediate development version.
|
|
|
|
|
|
|
|
|
|
|
|
To maintain compatibility, use [HsVersions.h](commentary/coding-style#) (see below) where possible, and try to avoid using \#ifdef in the source itself.
|
|
|
|
|
|
|
|
### `HsVersions.h`
|
|
|
|
|
|
|
|
`HsVersions.h` is a CPP header file containing a number of macros that help smooth out the differences between compiler versions. It defines, for example, macros for library module names which have moved between versions. Take a look [compiler/HsVersions.h](https://gitlab.haskell.org/ghc/ghc/blob/master/compiler/HsVersions.h).
|
|
|
|
|
|
|
|
```c
|
|
|
|
#include "HsVersions.h"
|
|
|
|
```
|
|
|
|
|
|
|
|
### The C Preprocessor (CPP)
|
|
### The C Preprocessor (CPP)
|
|
|
|
|
|
|
|
|
|
Whenever possible we try to avoid using CPP, as it can hide code from the compiler (which means changes that work on one platform can break the build on another) and code using CPP can be harder to understand.
|
|
Whenever possible we try to avoid using CPP, as it can hide code from the compiler (which means changes that work on one platform can break the build on another) and code using CPP can be harder to understand.
|
|
|
|
|
|
|
|
|
|
The following CPP symbols are used throughout the compiler:
|
|
The following CPP symbols are used throughout the compiler:
|
|
|
|
|
|
* `DEBUG`
|
|
* `DEBUG`
|
... | @@ -348,23 +311,11 @@ The following CPP symbols are used throughout the compiler: |
... | @@ -348,23 +311,11 @@ The following CPP symbols are used throughout the compiler: |
|
|
|
|
|
Regarding performance, a good rule of thumb is that `DEBUG` shouldn't add more than about 10-20% to the compilation time. This is the case at the moment. If it gets too expensive, we won't use it. For more expensive runtime checks, consider adding a flag - see for example `-dcore-lint`.
|
|
Regarding performance, a good rule of thumb is that `DEBUG` shouldn't add more than about 10-20% to the compilation time. This is the case at the moment. If it gets too expensive, we won't use it. For more expensive runtime checks, consider adding a flag - see for example `-dcore-lint`.
|
|
|
|
|
|
**Trap, pitfall for using the `ASSERT` macro**:
|
|
|
|
|
|
|
|
|
|
|
|
The `ASSERT` macro uses CPP, and if you are unwise enough to try to write assertions using primed variables (`ASSERT (not $ intersectsBlockEnv b b')`), one possible outcome is that CPP silently fails to expand the ASSERT, and you get this very baffling error message:
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
Not in scope: data constructor `ASSERT'
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Now you can Google for this error message :-)
|
|
|
|
|
|
|
|
* `GHCI`
|
|
* `GHCI`
|
|
|
|
|
|
Enables GHCi support, including the byte code generator and interactive user interface. This isn't the default, because the compiler needs to be bootstrapped with itself in order for GHCi to work properly. The reason is that the byte-code compiler and linker are quite closely tied to the runtime system, so it is essential that GHCi is linked with the most up-to-date RTS. Another reason is that the representation of certain datatypes must be consistent between GHCi and its libraries, and if these were inconsistent then disaster could follow.
|
|
Enables GHCi support, including the byte code generator and interactive user interface. This isn't the default, because the compiler needs to be bootstrapped with itself in order for GHCi to work properly. The reason is that the byte-code compiler and linker are quite closely tied to the runtime system, so it is essential that GHCi is linked with the most up-to-date RTS. Another reason is that the representation of certain datatypes must be consistent between GHCi and its libraries, and if these were inconsistent then disaster could follow.
|
|
|
|
|
|
### Platform tests
|
|
|
|
|
|
|
|
|
|
### Platform tests
|
|
|
|
|
|
Please refer to [Platforms and Conventions](commentary/platform-naming) wiki page for an overview of how to handle target specific code in GHC. |
|
Please refer to [Platforms and Conventions](commentary/platform-naming) wiki page for an overview of how to handle target specific code in GHC. |