├── .github └── settings.yml ├── CONTRIBUTING.md ├── README.md ├── documentation └── code.md ├── project-management ├── Github.md └── team-workflow.md ├── review └── Haskell.md └── style └── Haskell.md /.github/settings.yml: -------------------------------------------------------------------------------- 1 | repository: 2 | has_wiki: false 3 | 4 | labels: 5 | - name: "duplicate" 6 | color: cfd3d7 7 | - name: "good first issue" 8 | color: 7057ff 9 | - name: "invalid" 10 | color: cfd3d7 11 | - name: "P0" 12 | color: b60205 13 | description: "blocker: fix immediately!" 14 | - name: "P1" 15 | color: d93f0b 16 | description: "critical: next release" 17 | - name: "P2" 18 | color: e99695 19 | description: "major: an upcoming release" 20 | - name: "P3" 21 | color: fbca04 22 | description: "minor: not priorized" 23 | - name: "P4" 24 | color: fef2c0 25 | description: "unimportant: consider wontfix or other priority" 26 | - name: "question" 27 | color: d876e3 28 | - name: "type: bug" 29 | color: 0052cc 30 | - name: "type: documentation" 31 | color: 0052cc 32 | - name: "type: feature request" 33 | color: 0052cc 34 | - name: "wontfix" 35 | color: ffffff 36 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributor's guide 2 | 3 | ## Bug Reports 4 | 5 | Please [open an issue][new-issue]. 6 | 7 | The more detailed your report, the faster it can be resolved. Once the 8 | bug has been resolved, the person responsible will tag the issue as 9 | _needs confirmation_ and assign the issue back to you. Once you have 10 | tested and confirmed that the issue is resolved, close the issue. If 11 | you are not a member of the project, you will be asked for 12 | confirmation and we will close it. 13 | 14 | [new-issue]: https://github.com/tweag/guides/issues/new 15 | 16 | ## Code 17 | 18 | 1. Explain your idea before getting started and discuss your plan with 19 | members of the team. The best way to do this is to create 20 | an [issue][issue-tracker] or comment on an existing issue. 21 | 1. Prepare a git commit with your change. Don't forget to 22 | add [tests][tests]. Update the [README.md](./README.md) and 23 | changelog if appropriate. 24 | 1. [Create a pull request][create-pull-request]. This will start the 25 | code review process. Enable "Allow edits from maintainers" if the 26 | branch is on a fork, so that reviewers can fix trivial problems 27 | (typos) directly. **All submissions, including submissions by 28 | project members, require review.** 29 | 1. You may be asked to make some changes. Continuous integration bots 30 | will test your change automatically on supported platforms. Once 31 | everything looks good, your change will be merged. The minimum 32 | criteria for acceptance are: 33 | 34 | * a patch should be a minimal and accurate answer to exactly one 35 | identified and agreed problem, 36 | * a patch that modifies a stable public API should not break existing 37 | applications unless there is overriding consensus on the value of 38 | doing this. 39 | * A patch must adhere to the [code style guidelines][style-guide] 40 | of the project. 41 | 42 | [create-pull-request]: https://help.github.com/articles/creating-a-pull-request/ 43 | [style-guide]: https://github.com/tweag/guides/blob/master/style/ 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Guides 2 | ====== 3 | 4 | Guides about designing, programming and deploying. 5 | 6 | Credits 7 | ------- 8 | 9 | Guides is maintained by [Tweag I/O](http://tweag.io/). 10 | 11 | Have questions? Need help? Tweet at [@tweagio](http://twitter.com/tweagio). 12 | 13 | License 14 | ------- 15 | 16 | Guides is Copyright © 2014-2018 EURL Tweag. It is distributed under the 17 | [Creative Commons Attribution 18 | License](http://creativecommons.org/licenses/by/3.0/). 19 | -------------------------------------------------------------------------------- /documentation/code.md: -------------------------------------------------------------------------------- 1 | # Code documentation guidelines 2 | 3 | This document discusses guidelines for writing package and module 4 | level documentation. For the sake of brevity, this guide collects 5 | criteria for completeness rather than clarity. See the 6 | [Resources](#resources) section for more information. 7 | 8 | We define code documentation of a package to be the comments in all 9 | the source files in the package, plus any examples, plus any text 10 | files explaining the package (e.g. the README). 11 | 12 | Code documentation should be useful to both users and maintainers of a 13 | package. Documentation must: 14 | 15 | - Provide an overview of what problem each package and module solves. 16 | - Point to examples and material explaining how to use a package. 17 | - Explain the strategy used to solve the problem. 18 | - Help locating the implementation of a specific piece of the design. 19 | - Explain the details of the implementation where the intention of the 20 | author is not readily apparent from reading the code. 21 | - Point to resources to learn the libraries and frameworks used by a package. 22 | 23 | ## Text files 24 | 25 | All packages should offer a README with a synopsis of what problem the 26 | package solves, how is the package used, and the strategy it uses to solve the 27 | problem. 28 | 29 | **Example** 30 | 31 | From the [inline-java](https://github.com/tweag/inline-java) README: 32 | 33 | The Haskell standard includes a native foreign function interface 34 | (FFI). Using it can be a bit involved and only C support is 35 | implemented in GHC. inline-java lets you call any JVM function 36 | directly, from Haskell, without the need to write your own foreign 37 | import declarations using the FFI. In the style of inline-c for C 38 | and inline-r for calling R, inline-java lets you name any function 39 | to call inline in your code. It is implemented on top of the 40 | [jni](jni) and [jvm](jvm) 41 | packages and makes use of 42 | [quasi-quotations](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#template-haskell-quasi-quotation). 43 | ... 44 | inline-java can be used to build a Haskell program which starts 45 | the JVM and communicates to it through the Java Native Interface 46 | (JNI). Much of the low level details of using the JNI are hidden 47 | from the user. 48 | 49 | It is also possible to have the JVM load a Haskell library and 50 | invoke Haskell functions that can interact with the JVM using java 51 | quasi-quotations, e.g. what 52 | [sparkle](https://github.com/tweag/sparkle) does. 53 | 54 | A java quasiquotation ensures that Haskell and Java agree on the 55 | values that are passed between the runtimes, causes the java 56 | compiler to produce the Java bytecode, and then generates the 57 | necessary JNI calls to load and execute the bytecode in the JVM. 58 | 59 | inline-java uses whatever java compiler is found in the PATH 60 | environment variable. 61 | 62 | In principle, the author is free to include more documentation in the 63 | README or to put it in separate files. If documentation is placed in other files 64 | though, these files should be referenced from the README. 65 | 66 | The README is considered to be the root of all the hierarchy of text 67 | files explaining a package. This hierarchy must include pointers to: 68 | 69 | - any training materials, 70 | - Tests could serve as examples 71 | - Explanatory notes might be included in the API reference 72 | documentation 73 | - Acceptance criteria: The materials give an overview of how the 74 | public functions and interfaces of the package are used together 75 | to solve the problem. 76 | - design notes, 77 | - They might live as comments in the code 78 | - Acceptance criteria 79 | - The notes give an overview of how the internal functions and 80 | interfaces of the package are combined to solve the problem. 81 | - The notes explain why the package is implemented as it is 82 | when there are other possible choices. 83 | - the main entry points in the code for understanding the 84 | implementation, 85 | - resources for learning the libraries and frameworks on which the 86 | package depends. 87 | 88 | **Example** 89 | 90 | For more details on how to use inline-java, there is this 91 | [tutorial](https://www.tweag.io/posts/2017-09-15-inline-java-tutorial.html). 92 | ... 93 | To start navigating the code, grep for the note "Implementation strategy", 94 | which at the time of this writing is in 95 | [Language.Java.Inline](src/Language/Java/Inline.hs). 96 | 97 | The links to jni, jvm and quasi-quotations in the GHC user guide 98 | provide further references to understanding Java, the JNI and 99 | quasi-quoters. 100 | 101 | Packages implementing servers, command line tools or other 102 | executables should explain how to configure and execute them. 103 | 104 | ## In-code documentation 105 | 106 | Information about the code can be conveyed in a few ways, presented 107 | here in order of preference. 108 | 109 | 1. Types 110 | 2. Function and variable names 111 | 3. Conventions 112 | 4. Comments (please refer to [Haddock documentation](https://haskell-haddock.readthedocs.io/en/latest/markup.html#)) 113 | 114 | Whatever the chosen way is, the following facts should be communicated 115 | for each definition in the code. 116 | 117 | ### Types/Classes 118 | 119 | - The meaning of the type 120 | - What do the values of the type represent in the problem domain? 121 | - If the type has type parameters 122 | - What are they meant to be instantiated with? 123 | - Do they correspond to some part of the problem domain as well? 124 | - Data constructors 125 | - How do they relate to the problem domain? 126 | - What do the fields represent? 127 | - Is the representation isomorphic to the domain entities or are 128 | there some values that are to be considered invalid? 129 | - Are there any properties relating the values of the various fields? 130 | - Stateful types (**example**: [handles](http://hackage.haskell.org/package/base-4.12.0.0/docs/System-IO.html#t:Handle)) 131 | - What are the valid states? 132 | - How do transitions occur? 133 | 134 | **Example** 135 | 136 | -- | An occurrence of a java quasi-quotation 137 | -- 138 | -- The polymorphic type parameter starts instantiated as a general 139 | -- Core Type, and later changes to @SomeSing JType@ when we analyze it 140 | -- (see 'unliftJTypes') ahead of generating the Java source code. 141 | data QQOcc a = QQOcc 142 | { resultType :: a 143 | , antiQuotationTypes :: [a] 144 | -- | The text between the brackets @[java| ... |]@ 145 | , input :: Text 146 | -- | The name of the method to generate for executing the quasiquotation 147 | , methodName :: Text 148 | -- | e.g. @[“$x”, “$y”]@ in @[java| … $x … $y ... |]@ 149 | , antiQuotationNames :: [Text] 150 | -- | The line number where the quasi-quotation appears as reported 151 | -- by 'Language.Haskell.TH.location' 152 | , lineNumber :: Integer 153 | } 154 | 155 | ### Functions/Methods 156 | 157 | - Which preconditions and postconditions do they have? 158 | - Are there any properties relating them to other functions? 159 | - Do they stand for any operation in the problem domain? 160 | 161 | **Examples** 162 | 163 | -- | Returns ... 164 | -- 165 | -- Precondition: The Core Type must be monomorphic and must be of kind JType. 166 | -- 167 | -- If mapping over QQOccs, use ‘unliftQQOccJTypes’ instead. 168 | unliftJType :: JTypeNames -> Type -> CoreM (SomeSing JType) 169 | 170 | -- | Returns ... 171 | -- 172 | -- If mapping over multiple arguments, use ‘unliftJTypes’ instead. 173 | unliftQQOccJTypes :: JTypeNames -> QQOcc Type -> CoreM (QQOcc (SomeSing JType)) 174 | 175 | -- | Equivalent to @findJTypeNames >>= mapM . unliftQQOccJType@ 176 | unliftJTypes :: [QQOcc Type] -> CoreM [QQOcc (SomeSing JType)] 177 | 178 | -- | The names of 'JType' data constructors 179 | data JTypeNames = JTypeNames 180 | { nameClass :: Name 181 | , nameIface :: Name 182 | , nameArray :: Name 183 | , nameGeneric :: Name 184 | , namePrim :: Name 185 | , nameVoid :: Name 186 | , namePrimBoolean :: Name 187 | , namePrimByte :: Name 188 | , namePrimChar :: Name 189 | , namePrimShort :: Name 190 | , namePrimInt :: Name 191 | , namePrimLong :: Name 192 | , namePrimFloat :: Name 193 | , namePrimDouble :: Name 194 | } 195 | 196 | ### Modules 197 | 198 | - What criteria should be used to decide if a function or interface 199 | belongs to the module? 200 | 201 | **Example** 202 | 203 | -- | Definitions that depend on compiler internals 204 | module Language.Java.Inline.Internal.Magic where 205 | ... 206 | 207 | ## The guidelines in practice 208 | 209 | When these guidelines should be applied to a code base which doesn't 210 | follow them, they could be incorporated together with new changes. 211 | 212 | - Newly added definitions (functions, types, classes and modules) 213 | should conform to the guidelines. 214 | - Old definitions which are modified should conform to the guidelines, 215 | with the exception of refactorings or fixes which preserve the 216 | meaning of functions. 217 | - Definitions which are moved from one place to another unmodified 218 | don't need to conform. 219 | - The text documentation of newly added packages should conform to 220 | the guidelines. 221 | - The text documentation of existing packages that are modified should 222 | conform to the guidelines as much as the change is concerned. That 223 | is, the hierarchy of text files needs to be fleshed out sufficiently 224 | so the new state can be communicated to a person unfamiliar with the 225 | package. Examples: 226 | - The scope of the solution a package implements changes as a 227 | result of adding or removing definitions. 228 | - The way in which a package solves the problem changes as the 229 | result of a total or partial redesign. 230 | 231 | In particular, small bug-fixes don't need text documentation to be 232 | updated unless it is somehow invalidated by the fix. 233 | 234 | ## Resources 235 | 236 | * [A Haskell style guide](https://github.com/tweag/guides/tree/master/style/Haskell.md) 237 | * [Posts](http://jacobian.org/writing/great-documentation/) advising on how to write good documentation 238 | * [A recorded presentation](https://www.youtube.com/watch?v=8TD-20Mb_7M) at ZuriHack 2018 on how to write good documentation 239 | 240 | ## Improving this document 241 | 242 | It has been suggested that examples based on other languages than 243 | Haskell could be illustrative. If you find any cases where the 244 | advice in this guide is not readily applicable, please, consider 245 | submitting a pull request. 246 | -------------------------------------------------------------------------------- /project-management/Github.md: -------------------------------------------------------------------------------- 1 | # Managing public (or private) projects using GitHub 2 | 3 | We use a common set of labels across projects in GitHub. It is 4 | possible to precisely control this set of labels using 5 | the [settings GitHub app][github-app-settings] (needs to be enabled 6 | for your org). Use the [`settings.yml`](../.github/settings.yml) from 7 | this repository as a starting point. 8 | 9 | [github-app-settings]: https://github.com/probot/settings 10 | 11 | ## Labels 12 | 13 | We use labels to classify issues by priority, from P0 to P4: 14 | 15 | * **P0 (blocker)** Do not do anything else, this is a blocker. Major 16 | broken functionality that causes a release to be unusable. This 17 | includes regressions introduced in a new release that blocks 18 | a significant number of users, or an incompatible breaking change. 19 | * **P1 (critical)** Must have. Critical feature request or bug fix 20 | which should be addressed in the next release, or a serious issue 21 | that impacts many users. Typically does not require immediate 22 | action. 23 | * **P2 (major)** Should have. Important feature or defect that is tied 24 | to roadmaps or upcoming releases. Moderate live issue in a released 25 | version that is inconvenient for a user that needs to be addressed 26 | in a upcoming release. 27 | * **P3 (minor)** Could have. Desirable enhancement or minor bug fix 28 | that will benefit a user. Not prioritized into roadmaps or any 29 | imminent release. 30 | * **P4 (trivial)** Someday/maybe. Consider closing as "wontfix". Can 31 | be kept open for a potential re-prioritization if more users are 32 | impacted. 33 | 34 | Labels are also useful to classify issues by type or by component. Use 35 | colours to distinguish whether a label designates a type, a component 36 | or something else. 37 | 38 | ## Milestones 39 | 40 | We gate issues with [milestones][github-milestones]. Milestones always 41 | have an associated due date. This due date is sometimes used for 42 | internal planning, and sometimes represent business constraints. Use 43 | the milestone description to make the meaning clear. 44 | 45 | [github-milestones]: https://help.github.com/articles/about-milestones/ 46 | 47 | ## Epics 48 | 49 | [Epics][atlassian-epic] map to ["projects"][github-projects]. All the 50 | issues that pertain to the same epic are added to one project. Enable 51 | automation so that issues are moved from column to column with minimum 52 | fuss. There is one project per epic. Use the epic title as the name of 53 | project. List the goals of the epic (ideally with acceptance criteria) 54 | in the project description. 55 | 56 | [atlassian-epic]: https://www.atlassian.com/agile/project-management/epics 57 | [github-projects]: https://help.github.com/en/articles/about-project-boards 58 | 59 | ## Project leads 60 | 61 | Each project must have a project lead (maintainer). By convention, the 62 | PL is whoever appears at the top 63 | of [`CODEOWNERS` file][github-codeowners], e.g.: 64 | 65 | ``` 66 | * @mboes 67 | ``` 68 | 69 | This has the effect that @mboes will automatically be tagged as 70 | a reviewer for all incoming PR's, unless later lines in the same file 71 | override that. 72 | 73 | [github-codeowners]: https://help.github.com/articles/about-codeowners/ 74 | 75 | ## Change management 76 | 77 | The default contribution process can be found in 78 | the [`CONTRIBUTING.md` file](../CONTRIBUTING.md) of this repository. 79 | Whatever the contribution process, this should 80 | be [documented][github-contributing]. 81 | 82 | A corollary of the default contribution process is that all 83 | significant changes (including bug fixes) should have an issue prior 84 | to the pull request being opened. Also, if you have write permission 85 | on a repository, don't fork to open pull requests. 86 | 87 | ## Releases 88 | 89 | Every public release must include a `CHANGELOG.md` file, in the format 90 | of [keepachangelog]. New entries to the changelog are integral parts 91 | of bug fix and feature PR's. Always try to link entries to issue 92 | numbers if a relevant issue exists. It's best to link issues as 93 | follows: 94 | 95 | ``` 96 | * Frobnicated knobs. See [#NNN](https://github.com/foocorp/bar/issues/NNN). 97 | ``` 98 | 99 | Each release has an associated tag of the form `v$VERSION`, GPG-signed 100 | by the project lead (use `git tag -s`). 101 | See [this guide][keybase-gpg-github] for setting up an identify if you 102 | don't already have one. 103 | 104 | [keepachangelog]: https://keepachangelog.com/en/1.0.0/ 105 | [keybase-gpg-github]: https://github.com/pstadler/keybase-gpg-github 106 | 107 | ## Documentation 108 | 109 | Every document has a *document owner*. The owner is usually specified 110 | in the document itself. By default, the project lead is the owner for 111 | all documentation in the project. 112 | 113 | We do not use [wikis][github-wiki] for documentation. The reasons for 114 | this are well explained [elsewhere][yesod-documentation-thoughts]. 115 | When using [GitHub pages][github-pages], we *do not* use a `gh-pages` 116 | branch to hold documentation source code separately. The reason is 117 | that this breaks the ability to update documentation atomically with 118 | the associated code changes. We typically keep the documentation in 119 | a `docs/` folder. The *source* for documentation should be kept in the 120 | same branch than the code that it documents. This can readily be 121 | deployed to a public site using [GitHub pages][github-pages-conf] 122 | or [Read the Docs][readthedocs]. 123 | 124 | [github-pages]: https://pages.github.com/ 125 | [github-pages-conf]: https://help.github.com/articles/configuring-a-publishing-source-for-github-pages/ 126 | [github-wiki]: https://help.github.com/articles/about-github-wikis/ 127 | [readthedocs]: https://docs.readthedocs.io/en/latest/getting_started.html 128 | [yesod-documentation-thoughts]: https://www.yesodweb.com/blog/2015/08/thoughts-on-documentation 129 | -------------------------------------------------------------------------------- /project-management/team-workflow.md: -------------------------------------------------------------------------------- 1 | # Working with a team 2 | 3 | Here are a few rules for working effectively in a team: 4 | 5 | * Pull requests (PRs) should be reviewed. Having another pair of eyes looking 6 | at code changes is the best way to get rid of bugs due to inattention. 7 | Moreover the reviewer may suggest improvements which you can learn from. 8 | Finally it ensures that someone else can actually read and understand the 9 | code that you wrote. 10 | * Make your PRs as focused/concise/small as possible. This 11 | helps avoid rebasing issues down the road. By clarifying the intent this will 12 | also allow the reviewer to provide a more meaningful review. Moreover, if the PR is large, the 13 | reviewer will not be able to keep all the code changes in mind. Finally if 14 | the PR takes too long to review the reviewer may not be able to be focus 15 | through the whole set of changes. 16 | * Do not ask for a review until your code is working (CI passed) 17 | and actually ready for review (no "WIP", documentation-lacking PRs). On the 18 | one hand, it's rude and will waste the reviewer's time if they have to 19 | perform several review passes, and will have them context switch many times. 20 | On the other hand GitHub does not track what changes have already been 21 | reviewed, which can lead to someone reviewing the same snippet several times 22 | -- or worse, not reviewing it at all. 23 | * Make sure that only one person is working on a particular problem at a time. 24 | Do this by making sure everyone knows when you start working on something and 25 | what your goal is. This can be done through assignments on tickets or by 26 | synchronizing on Slack/etc. This also avoids the extreme case: working on 27 | something that has been done already. 28 | * If someone is blocked on a task that only you can fix to you, work on that 29 | task first. 30 | * Push your code as soon as possible if other people depend on it. 31 | -------------------------------------------------------------------------------- /review/Haskell.md: -------------------------------------------------------------------------------- 1 | Code review checklist 2 | ===================== 3 | 4 | When reviewing Haskell code, do watch out for the following points. 5 | 6 | ### Copyright notices at the top of every module 7 | 8 | If required by the product owner, check for a copyright notice. 9 | 10 | ```Haskell 11 | -- | 12 | -- Copyright: (C) 13 | -- 14 | -- Module description 15 | 16 | {-# LANGUAGE ... #-} 17 | {-# OPTIONS_GHC ... #-} 18 | module M where 19 | 20 | ... 21 | ``` 22 | 23 | ### Inverted boolean conditions 24 | 25 | Functions like 26 | ```Haskell 27 | filter :: (a -> Bool) -> [a] -> [a] 28 | break :: (a -> Bool) -> [a] -> ([a],[a]) 29 | when :: Monad m => Bool -> m () 30 | or 31 | and 32 | if-then-else 33 | ``` 34 | take a boolean predicate. Ensure the boolean predicate must not compute 35 | the opposite of what's intended. For instance, if the intention is to 36 | remove from a list all the occurrences of 0, a wrong solution is 37 | `filter (==0) [0,1,0,1]` the correct one being 38 | `filter (/=0) [0,1,0,1]`. 39 | 40 | ### Boundaries of enumerations and recursive functions 41 | 42 | The limits in expressions of the form `[x,x-1..y]`, `[x..]`, `[x..y]` 43 | must be checked to be correct. 44 | 45 | ### Termination of recursive functions 46 | 47 | Check that all recursive functions terminate (well-foundedness). The 48 | arguments of each recursive call must ensure progress towards the base 49 | case, and the base case has to be defined! 50 | 51 | ### Race conditions solved by locks 52 | 53 | When `MVar`s are used as a locking mechanisms it should be checked that 54 | the resources for which the `MVar` is intended are really protected, 55 | and furthermore that such protection is not redundant with other 56 | locks/`MVar`s. 57 | 58 | ### Unrevertable changes under `MVar` 59 | 60 | When an exception is thrown during an `MVar` action (`modifyMVar`, 61 | `withMVar`..) the `MVar` is reverted to its previous state. However, 62 | this behaviour may lead to an inconsistent state in the presence of 63 | other side effects, e.g. network actions or changes in other mutable 64 | variables. 65 | 66 | ### Laziness when copying non-garbage-collected data 67 | 68 | If the program manipulates memory which is released regardless of the 69 | references that exist to it, care must be taken to ensure that 70 | references do not exist after the memory is released. 71 | 72 | This happens often in Haskell bindings to OS or hardware resources 73 | which the garbage collector does not manage. Say we have a list of 74 | `bytestrings xs :: [ByteString]` where the memory pointed by the 75 | bytestrings can be released before the bytestrings are garbage 76 | collected. Attempting to copy the data before the memory is released is 77 | not well done with `map copy xs` since it defers copying until the 78 | result of the map is needed. The good solution would be more strict: 79 | `mapM (evaluate . copy) xs`. 80 | 81 | ### `foldl`: just say no - use `foldl'` 82 | 83 | `foldl` is approximately *never* appropriate. See 84 | [here][well-typed-foldl] for a longer discussion. Executive summary: 85 | `foldl f z xs` leads to space leaks, even when `f` is strict in both 86 | arguments. 87 | 88 | [well-typed-foldl]: http://www.well-typed.com/blog/90/ 89 | 90 | ### Use strict versions of containers where appropriate 91 | 92 | In most cases strict version of containers (e.g. `Data.Map.Strict`) are 93 | prefered over the lazy version - they perform better in most cases and 94 | make space leaks less likely. 95 | 96 | ### List prefix memory leaks 97 | 98 | Evaluating say 99 | ```Haskell 100 | case take n xs of 101 | (_:xs') -> ... xs' ... 102 | ``` 103 | may keep all of `xs` in memory until the end of `xs'` is evaluated. 104 | 105 | This memory leak can be fixed with 106 | ```Haskell 107 | case Control.DeepSeq.force $ take n xs of 108 | (_:xs') -> ... xs' ... 109 | ``` 110 | though the above also forces evaluation of the list elements, which is 111 | often overkill. The following can be enough: 112 | ```Haskell 113 | forceSpine xs = length xs `seq` xs 114 | 115 | case forceSpine $ take n xs of 116 | (_:xs') -> ... xs' ... 117 | ``` 118 | Or use `seqList` from the `parallel` package. Or better yet, use 119 | a strict list datatype rather than the standard lazy one. 120 | 121 | ### Names of intermediate computations 122 | 123 | When reviewing code like 124 | ```Haskell 125 | let x' = f x 126 | x'' = g x' 127 | in x'' 128 | ``` 129 | ensure that the arguments given to the intermediate computations `f` 130 | and `g` are the intended ones. Thus we could catch uses of, say, `x` 131 | where `x'` was intended. 132 | 133 | *Note:* the above is bad style. Such code should be written 134 | pointlessly, with `f` and `g` chained. Or pointfully, but then with 135 | meaningful names for the intermediate state rather than chains of 136 | apostrophes. 137 | 138 | ### Catamorphisms 139 | 140 | Use of catamorphisms (e.g. `foldr`, `foldl`) and other special case 141 | higher-order functions (such as `map`) is preferable to writing 142 | structurally recursive functions with explicit recursion. This is 143 | because a fold makes it a type error to forget the base case - 144 | a problem with the code that could otherwise go undetected. It also 145 | documents the fact that the function is supposed to be structurally 146 | recursive, as opposed to functions that use general recursion. 147 | 148 | ### Data types 149 | 150 | Constructor fields should be strict, unless there's an explicit reason 151 | to make them lazy. This avoids many common pitfalls caused by too much 152 | laziness and reduces the number of brain cycles the programmer has to 153 | spend thinking about evaluation order. 154 | 155 | ```Haskell 156 | -- Good 157 | data Point = Point 158 | { pointX :: !Double -- ^ X coordinate 159 | , pointY :: !Double -- ^ Y coordinate 160 | } 161 | 162 | -- Bad 163 | data Point = Point 164 | { pointX :: Double -- ^ X coordinate 165 | , pointY :: Double -- ^ Y coordinate 166 | } 167 | ``` 168 | 169 | Additionally, unpacking simple fields often improves performance and 170 | reduces memory usage: 171 | 172 | ```Haskell 173 | data Point = Point 174 | { pointX :: {-# UNPACK #-} !Double -- ^ X coordinate 175 | , pointY :: {-# UNPACK #-} !Double -- ^ Y coordinate 176 | } 177 | ``` 178 | 179 | ### Functions 180 | 181 | Have function arguments be lazy unless you explicitly need them to be 182 | strict. 183 | 184 | The most common case when you need strict function arguments is in 185 | recursion with an accumulator: 186 | 187 | ```Haskell 188 | mysum :: [Int] -> Int 189 | mysum = go 0 190 | where 191 | go !acc [] = acc 192 | go acc (x:xs) = go (acc + x) xs 193 | ``` 194 | -------------------------------------------------------------------------------- /style/Haskell.md: -------------------------------------------------------------------------------- 1 | # Haskell Style Guide 2 | 3 | (Originally from [Johan Tibell's style guide][tibbe-style-guide], with 4 | modifications from [the Snap framework's style 5 | guide][snap-style-guide], additions from the [GHC Coding Style 6 | Guidelines][ghc-style] and further inspiration from the style used at 7 | [Well-Typed LLP][well-typed], as seen e.g. in 8 | [distributed-process][distributed-process].) 9 | 10 | This is a short document describing the preferred Haskell coding style 11 | at Tweag I/O. When something isn't covered by this guide you should 12 | stay consistent with the code in the other modules. 13 | 14 | [tibbe-style-guide]: 15 | http://github.com/tibbe/haskell-style-guide/blob/master/haskell-style.md 16 | [snap-style-guide]: 17 | http://snapframework.com/docs/style-guide 18 | [ghc-style]: 19 | http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle 20 | [well-typed]: 21 | http://well-typed.com 22 | [distributed-process]: 23 | http://hackage.haskell.org/package/distributed-process 24 | 25 | ## General Style 26 | 27 | (section adapted from the [GHC Coding Style Guidelines][ghc-style].) 28 | 29 | It's much better to write code that is transparent than to write code 30 | that is short. 31 | 32 | Often it's better to write out the code longhand than to reuse 33 | a generic abstraction (not always, of course). Sometimes it's better 34 | to duplicate some similar code than to try to construct an elaborate 35 | generalisation with only two instances. Remember: other people have to 36 | be able to quickly understand what you've done, and overuse of 37 | abstractions just serves to obscure the really tricky stuff. 38 | 39 | The general rule is to stick to the same coding style as is already 40 | used in the file you're editing. If you must make stylistic changes, 41 | commit them separately from functional changes, so that someone 42 | looking back through the change logs can easily distinguish them. 43 | 44 | ## Formatting 45 | 46 | ### Line Length 47 | 48 | Maximum line length is *80 characters*. Comments should be wrapped 49 | accordingly. There should be no trailing whitespace anywhere in your 50 | code. This makes git diff output look ugly and causes spurious merge 51 | conflicts. 52 | 53 | In Emacs, you can add the following code to your `init.el` file to 54 | enforce this: 55 | 56 | ```elisp 57 | (add-hook 'haskell-mode-hook (lambda () (set-fill-column 80))) 58 | (add-hook 'haskell-mode-hook 59 | (lambda () 60 | (add-hook 'before-save-hook 'delete-trailing-whitespace t t))) 61 | ``` 62 | 63 | ### Indentation 64 | 65 | Tabs are illegal. Use spaces for indenting. Use *2 spaces* for each 66 | indentation level. The only exception is for code blocks inside 67 | a definition, which should be indented with *4 spaces*. Indent the 68 | `where` keyword two spaces to set it apart from the rest of the code 69 | and indent the definitions in a `where` clause 2 spaces. Guards are 70 | usually indented 2 spaces. Some examples: 71 | 72 | ```Haskell 73 | sayHello :: IO () 74 | sayHello = do 75 | name <- getLine 76 | putStrLn $ greeting name 77 | where 78 | greeting name = "Hello, " ++ name ++ "!" 79 | 80 | filter :: (a -> Bool) -> [a] -> [a] 81 | filter _ [] = [] 82 | filter p (x:xs) 83 | | p x = x : filter p xs 84 | | otherwise = filter p xs 85 | ``` 86 | 87 | As a general rule, *indentation of a line should not depend on the 88 | length of any identifier in preceding lines*, only on layout 89 | constraints. 90 | 91 | ### Blank Lines 92 | 93 | *One blank line* between top-level definitions. *No blank lines* 94 | between type signatures and function definitions. Add one blank line 95 | between functions in a type class instance declaration if the 96 | functions bodies are large. Use your judgement. 97 | 98 | ### Whitespace 99 | 100 | Surround binary operators with a single space on either side. Use your 101 | better judgement for the insertion of spaces around arithmetic 102 | operators but always be consistent about whitespace on either side of 103 | a binary operator. Don't insert a space after a lambda. Don't insert 104 | space inside a parenthesized expression. 105 | 106 | Don't vertically align type signatures, patterns in function 107 | declarations and comments. This just causes spurious merge conflicts. 108 | 109 | ### Applications 110 | 111 | If an application must spawn multiple lines to fit within the maximum 112 | line length, then write one argument on each line following the head, 113 | indented by one level: 114 | 115 | ```Haskell 116 | let'sFold xs = do 117 | foldr 118 | (\x y -> ...) 119 | Nothing 120 | xs 121 | ``` 122 | 123 | But consider naming the arguments instead to avoid multi-line 124 | expressions. 125 | 126 | ### Data Declarations 127 | 128 | Align the constructors in a data type definition. Example: 129 | 130 | ```Haskell 131 | data Tree a 132 | = Branch !a !(Tree a) !(Tree a) 133 | | Leaf 134 | ``` 135 | 136 | Format records as follows: 137 | 138 | ```Haskell 139 | data Person = Person 140 | { firstName :: !String -- ^ First name 141 | , lastName :: !String -- ^ Last name 142 | , age :: !Int -- ^ Age 143 | } deriving (Eq, Show) 144 | ``` 145 | 146 | ### List Declarations 147 | 148 | Align the elements in the list. Example: 149 | ```Haskell 150 | exceptions = 151 | [ InvalidStatusCode 152 | , MissingContentHeader 153 | , InternalServerError 154 | ] 155 | ``` 156 | 157 | ### Pragmas 158 | 159 | Put pragmas immediately following the function they apply to. Example: 160 | 161 | ```Haskell 162 | id :: a -> a 163 | id x = x 164 | {-# INLINE id #-} 165 | ``` 166 | 167 | In the case of data type definitions you must put the pragma before 168 | the type it applies to. Example: 169 | 170 | ```Haskell 171 | data Array e = Array {-# UNPACK #-} !Int !ByteArray 172 | ``` 173 | 174 | `LANGUAGE` pragmas should enable a single language extension per line, 175 | for easy addition and deletion. 176 | 177 | ### Hanging Lambdas 178 | 179 | You may or may not indent the code following a "hanging" lambda. Use 180 | your judgement. Some examples: 181 | 182 | ```Haskell 183 | bar :: IO () 184 | bar = 185 | forM_ [1, 2, 3] $ \n -> do 186 | putStrLn "Here comes a number!" 187 | print n 188 | 189 | foo :: IO () 190 | foo = 191 | alloca 10 $ \a -> 192 | alloca 20 $ \b -> 193 | cFunction a b 194 | ``` 195 | 196 | ### Export Lists 197 | 198 | Format export lists as follows: 199 | 200 | ```Haskell 201 | module Data.Set 202 | ( -- * The @Set@ type 203 | Set 204 | , empty 205 | , singleton 206 | -- * Querying 207 | , member 208 | ) where 209 | ``` 210 | 211 | ### If-then-else expressions 212 | 213 | Generally, guards should be preferred over if-then-else expressions, 214 | where possible. if-then-else is preferred to case analysis on 215 | a boolean. Short cases should usually be put on a single line (when 216 | line length allows it). 217 | 218 | When writing non-monadic code (i.e. when not using `do`) and guards 219 | can't be used, you can align if-then-else expressions like you would 220 | normal expressions: 221 | 222 | ```Haskell 223 | foo = 224 | if ... 225 | then ... 226 | else ... 227 | ``` 228 | 229 | In monadic code, so long as you use the Haskell 2010 dialect and 230 | above, you can use the same alignment as above. A different alignment 231 | rule for monadic code is no longer necessary. 232 | 233 | ### Case expressions 234 | 235 | The alternatives in a case expression can be indented using either of 236 | the two following styles: 237 | 238 | ```Haskell 239 | foobar = case something of 240 | Nothing -> foo 241 | Just j -> bar 242 | ``` 243 | 244 | or as 245 | 246 | ```Haskell 247 | foobar = 248 | case something of 249 | Nothing -> foo 250 | Just j -> bar 251 | ``` 252 | 253 | ## Imports 254 | 255 | Imports should be listed in alphabetical order with no intervening 256 | blank lines, except for any explicit `Prelude` import, which must 257 | always come last. The reason for this exception is that some redundant 258 | import warnings are sensitive to the order of the `Prelude` import. 259 | 260 | Always use explicit import lists or `qualified` imports for standard 261 | and third party libraries. This makes the code more robust against 262 | changes in these libraries. Exception: the Prelude. 263 | 264 | Use your judgement when it comes to local application/library specific 265 | imports. On the one hand, they make for more maintainable code because 266 | identifiers that are removed from the imported module will be caught 267 | early and identifiers added to the imported module do not risk 268 | clashing with local identifiers. They also serve as documentation as 269 | to which parts of a module are actually required. 270 | 271 | However, explicit import lists are also much more verbose, and slow 272 | down development. Moreover, in a collaborative environment, explicit 273 | import lists can cause spurious conflicts, since two otherwise 274 | unrelated changes to a file may both require changes to the same 275 | import list. 276 | 277 | The qualifier for well known modules, such as `ByteString` can be 278 | shortened further, eg `BS`. But in general, prefer descriptive 279 | qualifiers rather than one letter ones. For example 280 | 281 | ```Haskell 282 | import qualified Data.Map as Map -- good 283 | import qualified Data.Map as M -- not so good 284 | ``` 285 | 286 | ## Comments 287 | 288 | Comments should be placed immediately *before* the line(s) of code 289 | they pertain to. 290 | 291 | ### Punctuation 292 | 293 | Write proper sentences; start with a capital letter and use proper 294 | punctuation. 295 | 296 | ### End-of-line comments 297 | 298 | End-of-line comments should be separated from code by at least two 299 | spaces. 300 | 301 | ### Copyright notices 302 | 303 | A copyright notice, if any is desired, should appear at the very first 304 | non-blank line in the file, so as to make its scope clear and the 305 | notice easy to find. Use [Haddock fields][haddock-fields] to include 306 | it in API documentation. Example: 307 | 308 | ``` 309 | -- | 310 | -- Copyright: (c) 1901-1910 Foo corp 311 | -- License: All rights reserved. 312 | ``` 313 | 314 | [haddock-fields]: https://www.haskell.org/haddock/doc/html/ch03s03.html 315 | 316 | ### Top-Level Definitions 317 | 318 | Comment every top-level function (particularly exported functions), 319 | and provide a type signature; use Haddock syntax in the comments. 320 | Comment every exported data type. Function example: 321 | 322 | ```Haskell 323 | -- | Send a message on a socket. The socket must be in a connected 324 | -- state. Returns the number of bytes sent. Applications are 325 | -- responsible for ensuring that all data has been sent. 326 | send 327 | :: Socket -- ^ Connected socket 328 | -> ByteString -- ^ Data to send 329 | -> IO Int -- ^ Bytes sent 330 | ``` 331 | 332 | For functions the documentation should give enough information to 333 | apply the function without looking at the function's definition. 334 | 335 | Record example: 336 | ```Haskell 337 | -- | Bla bla bla. 338 | data Person = Person 339 | { age :: !Int -- ^ Age 340 | , name :: !String -- ^ First name 341 | } 342 | ``` 343 | For fields that require longer comments format them like so: 344 | 345 | ```Haskell 346 | data Record = Record 347 | { -- | This is a very very very long comment that is split over 348 | -- multiple lines. 349 | field1 :: !Text 350 | -- | This is a second very very very long comment that is split 351 | -- over multiple lines. 352 | , field2 :: !Int 353 | } 354 | ``` 355 | 356 | ### Links 357 | 358 | Use inline identifier links economically. You are encouraged to add 359 | links for API names. It is not necessary to add links for all API 360 | names in a Haddock comment. We therefore recommend adding a link to an 361 | API name if: 362 | 363 | * The user might actually want to click on it for more information 364 | (use your judgment), and 365 | * Only for the first occurrence of each API name in the comment (don't 366 | bother repeating a link) 367 | 368 | ## Naming 369 | 370 | Use mixed-case when naming functions and camel-case when naming data 371 | types. 372 | 373 | For readability reasons, don't capitalize all letters when using an 374 | abbreviation. For example, write `HttpServer` instead of `HTTPServer`. 375 | Exception: Two letter abbreviations, e.g. `IO`. 376 | 377 | ### Records 378 | 379 | Where appropriate, add an unabbreviated prefix to the name of record 380 | fields. Example: 381 | 382 | ```Haskell 383 | -- | Messages consist of their typeRep fingerprint and their encoding 384 | data Message = Message 385 | { messageFingerprint :: !Fingerprint 386 | , messageEncoding :: !BSL.ByteString 387 | } 388 | ``` 389 | 390 | This is not necessary for modules that export only one data type *and* 391 | are meant to be imported qualified. 392 | 393 | ### Modules 394 | 395 | Use singular when naming modules e.g. use `Data.Map` and 396 | `Data.ByteString.Internal` instead of `Data.Maps` and 397 | `Data.ByteString.Internals`. 398 | 399 | ## Misc 400 | 401 | ### Warnings 402 | 403 | Code should be compilable with `-Wall -Werror` at a minimum, i.e. 404 | there should be no warnings. `-Wall` does not turn on all warnings, so 405 | the following set: 406 | 407 | ``` 408 | -Wall 409 | -Wcompat 410 | -Wincomplete-record-updates 411 | -Wincomplete-uni-patterns 412 | -Wredundant-constraints 413 | -Wnoncanonical-monad-instances 414 | ``` 415 | 416 | ### Debug facilities 417 | 418 | Use of `unsafePerformIO`-based debugging facilites, such as 419 | `Debug.Trace` should not be committed to any stable branches. If you 420 | need logging, use a proper method and ship it, so that further 421 | development benefits from your debugging efforts. 422 | 423 | ### Lenses 424 | 425 | Where appropriate, define lenses for all fields in a record. Use the 426 | `_` prefix to name fields. When using the [lens package][lens], use 427 | `makeClassy` [where possible][lens-makeClassy] to generate lenses 428 | rather than `makeLenses`. This is to make it easy to export all lenses 429 | for a record all at once. 430 | 431 | ```Haskell 432 | module Person 433 | ( Person(..) 434 | , HasPerson(..) 435 | ) where 436 | 437 | data Person = Person 438 | { _firstName :: !String -- ^ First name 439 | , _lastName :: !String -- ^ Last name 440 | , _age :: !Int -- ^ Age 441 | } deriving (Eq, Show) 442 | 443 | makeClassy ''Person 444 | ``` 445 | 446 | For consistency, if a record has lenses defined, always use the lens 447 | to get or set the field of a record (rather than `_fieldName`). Field 448 | names should only be used to initialize the record. 449 | 450 | [lens]: http://hackage.haskell.org/package/lens 451 | [lens-makeClassy]: http://hackage.haskell.org/package/lens-4.3.3/docs/Control-Lens-TH.html#v:makeClassy 452 | --------------------------------------------------------------------------------