mirror of
https://github.com/github/semantic.git
synced 2024-11-27 03:09:48 +03:00
Add contribution instructions
This commit is contained in:
parent
97e38ae1c1
commit
66bb52dc7f
35
CONTRIBUTING.md
Normal file
35
CONTRIBUTING.md
Normal file
@ -0,0 +1,35 @@
|
||||
## Contributing
|
||||
|
||||
[fork]: https://github.com/github/semantic-open-source/fork
|
||||
[pr]: https://github.com/github/semantic-open-source/compare
|
||||
[style]: docs/coding-style.md
|
||||
[code-of-conduct]: CODE_OF_CONDUCT.md
|
||||
|
||||
Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
|
||||
|
||||
Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE.md).
|
||||
|
||||
Please note that this project is released with a [Contributor Code of Conduct][code-of-conduct]. By participating in this project you agree to abide by its terms.
|
||||
|
||||
## Submitting a pull request
|
||||
|
||||
0. [Fork][fork] and clone the repository
|
||||
0. Configure and install the dependencies: `script/bootstrap`
|
||||
0. Make sure the tests pass on your machine: `cabal new-build`
|
||||
0. Create a new branch: `git checkout -b my-branch-name`
|
||||
0. Make your change, add tests, and make sure the tests still pass
|
||||
0. Push to your fork and [submit a pull request][pr]
|
||||
0. Pat your self on the back and wait for your pull request to be reviewed and merged.
|
||||
|
||||
Here are a few things you can do that will increase the likelihood of your pull request being accepted:
|
||||
|
||||
- Follow the [style guide][style].
|
||||
- Write tests.
|
||||
- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests.
|
||||
- Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
|
||||
|
||||
## Resources
|
||||
|
||||
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
|
||||
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
|
||||
- [GitHub Help](https://help.github.com)
|
165
docs/coding-style.md
Normal file
165
docs/coding-style.md
Normal file
@ -0,0 +1,165 @@
|
||||
Haskell is a syntactically-flexible language, which gives the programmer a tremendous amount of leeway regarding the appearance of their code. This is a set of best practices that we use in `semantic` and its related projects.
|
||||
|
||||
This file draws from the style guides written by [Johan Tibbel](https://github.com/tibbe/haskell-style-guide/blob/master/haskell-style.md) and [Kowainik](https://kowainik.github.io/posts/2019-02-06-style-guide).
|
||||
|
||||
# General guidelines
|
||||
|
||||
Make your code look like the code around it. Consistency is the name of the game.
|
||||
|
||||
Use `stylish-haskell` for general formatting. We have our own style defined in `.stylish-haskell.yaml`, and it automates many uninteresting style debates: it will format `LANGUAGE` pragmas, alphabetize and align imports, etc. Atom, Emacs, vim, and most other editors can run it automatically. You should also set up your editor to remove trailing whitespace.
|
||||
|
||||
Our CI systems ensure that all patches pass `hlint`'s muster. We have our own set of rules in `.hlint.yaml`.
|
||||
|
||||
We strongly recommend adding Haddock documentation to any function/data type, unless its purpose is immediately apparent from its name.
|
||||
Comments should describe the "why", type signatures should describe the "what", and the code should describe the "how".
|
||||
|
||||
The Haskell Prelude is too minimal for serious work. The `Prologue` module should be imported in most files, as it reexports most of what you need.
|
||||
|
||||
# Formatting
|
||||
|
||||
2 spaces everywhere. Tabs are forbidden. Haskell indentation can be unpredictable, so generally stick with what your editor suggests.
|
||||
There is no hard line-length limit, though if you go beyond 110 or 120 you should generally split it up, especially for type signatures.
|
||||
|
||||
### Use applicative notation when constructing simple data types.
|
||||
|
||||
``` haskell
|
||||
thing :: Parser Foo
|
||||
|
||||
-- Broke:
|
||||
thing = do
|
||||
a <- bar
|
||||
b <- baz
|
||||
pure (Foo a b)
|
||||
|
||||
-- Woke:
|
||||
thing = Foo <$> bar <*> baz
|
||||
```
|
||||
|
||||
Overreliance on applicative notation can create code that is difficult to read. Don't use applicative notation in combination with operator sections. If in doubt, write it with `do` notation and see if it's more immediately comprehensible.
|
||||
|
||||
Avoid the `Applicative` instance for functions. That means you, Rob.
|
||||
|
||||
### Use leading commas for records, exports, and lists.
|
||||
|
||||
Leading commas make it easy to add and remove fields without introducing syntax errors, and properly aligned records are easy to read:
|
||||
|
||||
``` haskell
|
||||
data Pos = Pos
|
||||
{ posLine :: Int
|
||||
, posColumn :: Int
|
||||
}
|
||||
```
|
||||
|
||||
### Split up imports into logical groups.
|
||||
|
||||
We use the following convention, each section separated by a newline:
|
||||
|
||||
1. Prelude/Prologue import
|
||||
2. Library/stdlib imports
|
||||
3. Local in-project imports.
|
||||
|
||||
### Align typographical symbols.
|
||||
|
||||
`->` in `case` statements and signatures, `=` in functions, and `::` in records should be aligned. Your editor can help with this. In certain situations, aligning symbols may decrease readability, e.g. complicated `case` statements. Use your best judgment.
|
||||
|
||||
# Naming
|
||||
|
||||
Locally bound variables (such as the arguments to functions, or helpers defined in a `where` clause) can have short names, such as `x` or `go`. Globally bound functions and variables should have descriptive names.
|
||||
|
||||
You'll often find yourself implementing functions that conflict with Prelude/Prologue definitions. If this is the case, avoid adding a prefix to these functions, and instead import them qualified.
|
||||
|
||||
``` haskell
|
||||
-- Broke
|
||||
foo = heapLookup thing
|
||||
-- Woke
|
||||
foo = Heap.lookup thing
|
||||
```
|
||||
|
||||
Unlike many Haskell projects, we rely in places on variable shadowing (especially in open-recursive functions).
|
||||
Avoid variable shadowing if possible, as it can lead to unintuitive error messages; you are free to disable shadowing on in a per-file basis with `{-# OPTIONS_GHC -Wshadow #-}`
|
||||
|
||||
# Functions
|
||||
|
||||
### Don't go buckwild with infix operators.
|
||||
|
||||
Sensible use of infix operators can provide serious readability benefits, but often the best tool is just a named function. If you're defining new operators, make sure that you have a solid justification for doing so.
|
||||
|
||||
### Avoid list comprehensions.
|
||||
|
||||
In almost all cases, `map`, `filter`, `fold`, and the `[]` monad are more flexible and readable.
|
||||
|
||||
### Don't go buckwild with point-free definitions.
|
||||
|
||||
Point-free style can help or hinder readability, depending on the context. If a function is expressed naturally with the `.` operator, then do so, but if you have to gyrate the definition to write it point-free, then you should probably just write out the variable names. If you are reviewing someone else's PR and find a point-free definition hard to read, ask them to simplify/clarify it.
|
||||
|
||||
### Prefer `.` and `$` to parentheses.
|
||||
|
||||
Parentheses can make a function harder to edit, since parentheses have to be balanced. The composition and application operators (`.` and `$`) can reduce clunkiness.
|
||||
|
||||
``` haskell
|
||||
-- Broke
|
||||
f (g (h x))
|
||||
-- Woke
|
||||
f $ g $ h x
|
||||
-- Bespoke
|
||||
f . g . h $ x
|
||||
```
|
||||
|
||||
### Do not use partial functions.
|
||||
|
||||
`hlint` will catch several classes of partial functions (`head`, `fromJust`, etc.). Do not use `error` if at all possible, and never use `undefined`.
|
||||
|
||||
# Data Types
|
||||
|
||||
### Prefer `newtype`s to `type`s.
|
||||
|
||||
`newtype` values are zero-cost to construct and eliminate, and provide more informative error messages than `type` synonyms. Only use `type` for convenience aliases to existing types.
|
||||
|
||||
### Don't use `String`.
|
||||
|
||||
`String` is almost always the wrong choice. If your type represents human-readable strings, use `Text`; if you have a blob of bytes, use `ByteString`. `-XOverloadedStrings` is enabled globally to make this easy.
|
||||
|
||||
### Use `-XDerivingStrategies` when using `-XGeneralizedNewtypeDeriving` or `-XDeriveAnyClass`.
|
||||
|
||||
Subtle bugs can creep in if you fail to specify the correct strategy, so prefer specifying an explicit strategy even if GHC doesn't require it.
|
||||
If all the classes you're `deriving` are the stock classes (`Eq`, `Ord`, `Show`, etc.), there's no need to specify a strategy.
|
||||
|
||||
### Only use record selectors on single-constructor types.
|
||||
|
||||
The following code generates two partial functions, which is bad:
|
||||
|
||||
``` haskell
|
||||
data Bad = Evil { getInt :: Int }
|
||||
| Bad { getFloat :: Float }
|
||||
```
|
||||
|
||||
If you need fields that properly take failure into account, consider using `lens` and generating `Lens`es and `Traversal`s, which avoid calls to `error`.
|
||||
|
||||
An exception to this case is when record selectors are present to provide clarity regarding field names. In this case, prefix the field names with `_` (so that GHC will avoid warning you), and export only the constructors, like so:
|
||||
|
||||
``` haskell
|
||||
module Thing (Foo (Bar, Baz)) where
|
||||
|
||||
data Foo = Bar { _thing1 :: String
|
||||
, _thing2 :: String
|
||||
}
|
||||
| Baz { _thingRed :: Float
|
||||
, _thingBlue :: Float
|
||||
}
|
||||
```
|
||||
|
||||
# `lens`
|
||||
|
||||
The `lens` library is large and intimidating, and poorly-written `lens` code can be impossible to maintain. Here are some suggestions to keep complexity at bay:
|
||||
|
||||
* Prefer hand-written lenses to those generated by Template Haskell, except if the hand-written lens is significantly less reasonable than a TH splice.
|
||||
* Only use these infix operators: `^.` (`view`), `^?` (`preview`), `.~` (`set`), `%~` (`over`), and `^..` (`toListOf`). Prefer prefix functions in other cases.
|
||||
* Prefer specific imports: if all you need is `^.`, import just `Control.Lens.Getter` rather than `Control.Lens`.
|
||||
* Only use lenses when you have to view and manipulate deeply nested data types. If you can get away with a plain old record, do so.
|
||||
|
||||
# Miscellany
|
||||
|
||||
* Prefer eliminators like `maybe` and `either` to explicit pattern-matching.
|
||||
* Prefer `guards` and the `bool` eliminator to if-then-else statements.
|
||||
* Prefer `where` to `let`, except in the case of nested `where`s.
|
||||
* Don't use `{-# ANN … #-}` to disable hlint warnings, as it can slow down compilation. If you need to disable lints in a file, do so in `.hlint.yaml`.
|
Loading…
Reference in New Issue
Block a user