relude-0.6.0.0: README.md
# relude

[](https://github.com/kowainik/relude/actions)
[](http://travis-ci.org/kowainik/relude)
[](https://ci.appveyor.com/project/kowainik/relude)
[](https://hackage.haskell.org/package/relude)
[](http://stackage.org/lts/package/relude)
[](http://stackage.org/nightly/package/relude)
[](https://opensource.org/licenses/MIT)
**`relude`** is an alternative prelude library. If you find the default
`Prelude` unsatisfying, despite its advantages, consider using `relude` instead.
Below you can find key design principles behind `relude`:
1. **Avoid all** [**partial functions**](https://www.reddit.com/r/haskell/comments/5n51u3/why_are_partial_functions_as_in_head_tail_bad/)
(like `head :: [a] -> a`). The types of partial functions lie about their
behavior and usage of such functions can lead to the unexpected bugs. Though
you can still use some unsafe functions from `Relude.Unsafe` module, but they
are not exported by default.
2. **Type-safety**. We like to make invalid states unrepresentable. And if it's
possible to express this concept through the types then we do it.
_Example:_
```haskell
whenNotNull :: Applicative f => [a] -> (NonEmpty a -> f ()) -> f ()
```
instead of
```haskell
whenNotNull :: Applicative f => [a] -> ([a] -> f ()) -> f ()
```
3. **Performance.** Prefer `Text` over [`String`](https://www.reddit.com/r/haskell/comments/29jw0s/whats_wrong_with_string/),
use spaceleak-free functions (like our custom `sum` and `product`), introduce
`{-# INLINE #-}` and `{-# SPECIALIZE #-}` pragmas where appropriate.
4. **Minimalism** (low number of dependencies). We don't force users of `relude` to
stick to some specific lens or text formatting or logging library. If
possible, `relude` tries to depend only on boot libraries.
[Dependency graph](relude-dependency-graph.pdf) of `relude` can give you clearer picture.
5. **Convenience** (e.g lifted to `MonadIO` functions, more reexports). Despite minimalism, we
want to bring common types and functions (like `containers` and `bytestring`)
into scope because they are used in almost every application anyways.
6. **Provide excellent documentation.**
+ Tutorial
+ Migration guide from `Prelude`
+ Haddock for every function with examples tested by [`doctest`](http://hackage.haskell.org/package/doctest)
+ Documentation on [internal module structure](http://hackage.haskell.org/package/relude/docs/Relude.html)
+ `relude`-specific [HLint](http://hackage.haskell.org/package/hlint) rules: [`.hlint.yaml`](.hlint.yaml)
7. **User-friendliness.** Ability to quickly migrate to `relude` if you're familiar
with the common libraries like `text` and `containers`.
8. **Exploration.** Experiment with new ideas and proposals without introducing
breaking changes. `relude` uses the approach with `Extra.*` modules which are not
exported by default so it's quite easy to bring something new and let users
decide to use it or not.
This README contains introduction to `relude` and a tutorial on how to use it.
## Structure of this tutorial
This tutorial has several parts:
1. [When to use an alternative prelude?](#when-to-use-an-alternative-prelude-)
2. [Get started](#get-started-)
* [base-noprelude](#base-noprelude-)
* [Mixins](#mixins-)
* [NoImplicitPrelude](#NoImplicitPrelude-)
3. [Difference from Prelude](#difference-from-prelude-)
4. [Reexports](#reexports-)
5. [What's new?](#whats-new-)
6. [Migration guide](#migration-guide-)
7. [Comparison with other alternative preludes](#comparison-with-other-alternative-preludes-)
* [Relude vs Protolude](#relude-vs-protolude-)
8. [For developers](#for-developers-)
This is neither a tutorial on _Haskell_ nor tutorial on each function contained
in `relude`. For detailed documentation of every function together with examples
and usage, see [_Haddock documentation_](http://hackage.haskell.org/package/relude).
## When to use an alternative prelude? [↑](#structure-of-this-tutorial)
The module with the name `Prelude` is a module imported by default in every Haskell
source file of your project. If you want to use some data types or functions
which are not exposed by `Prelude`, you need to import them, adding necessary
libraries to your project dependencies. Unlike ordinary libraries, alternative
preludes provide different set of available by default functions and data types.
Replacing default `Prelude` from `base` has the following disadvantages:
1. Increased threshold entrance: you need to learn a different standard library.
+ `relude` tries to lower this threshold as much as possible: excellent
documentation, no custom abstractions, behavior is changed only for a small
subset of functions.
2. Extra dependencies: adding more libraries to dependencies increases build
times and maintenance burden.
+ `relude` depends only on boot libraries (almost) which results in small build time, follows
[PvP](https://pvp.haskell.org/) and cares about backwards compatibility.
However, using an alternative prelude, specifically `relude`, has the following
**advantages**:
1. Increased code safety: no partial functions, no space-leak functions.
2. Increased productivity: no need to import common functions and data types,
more common idioms provided.
3. Increased performance: some functions in `relude` are faster than in default `Prelude`.
Our recommendations when to use `relude`:
1. When you develop an application (e.g. CLI tool, web-app). In that case greater
productivity is more important than a low number of dependencies.
2. When writing a big framework. Some of them can be bigger than applications.
## Get started [↑](#structure-of-this-tutorial)
If you want to start using `relude` in your project and explore it with the help
of the compiler, set everything up according to one of the instructions below.
### base-noprelude [↑](#structure-of-this-tutorial)
This is the recommended way to use custom prelude. It requires you to perform
the following steps:
1. Replace the `base` dependency with corresponding version of `base-noprelude` in
your `.cabal` file.
2. Add a `relude` dependency to your `.cabal` file.
3. Add the following `Prelude` module to your project (both to filesystem and to `exposed-modules`):
```haskell
module Prelude
( module Relude
) where
import Relude
```
> **NOTE:** if you use [`summoner`](https://github.com/kowainik/summoner) to generate Haskell project,
> this tool can automatically create such structure for you when you specify custom prelude.
4. Optionally modify your `Prelude` to include more or less functions. Probably
you want to hide something from `Relude` module. Or maybe you want to add
something from `Relude.Extra.*` modules!
This is a very convenient way to add a custom prelude to your project because
you don't need to import module manually inside each file and enable the
`NoImplicitPrelude` extension.
### Mixins [↑](#structure-of-this-tutorial)
You can use Cabal feature `mixins` to replace the default `Prelude` with `Relude`
without need to add extra dependencies or import `Relude` manually each time.
See the following example:
> **NOTE:** this requires Cabal version to be at least `2.2`
```cabal
cabal-version: 2.2
name: prelude-example
version: 0.0.0.0
library
exposed-modules: Example
build-depends: base >= 4.10 && < 4.13
, relude ^>= 0.4.0
mixins: base hiding (Prelude)
, relude (Relude as Prelude)
default-language: Haskell2010
```
If you want to be able to import `Extra.*` modules when using `mixins` approach,
you need to list those modules under `mixins` field as well, like this:
```cabal
mixins: base hiding (Prelude)
, relude (Relude as Prelude, Relude.Extra.Enum)
```
### NoImplicitPrelude [↑](#structure-of-this-tutorial)
Disable the built-in prelude at the top of your file:
```haskell
{-# LANGUAGE NoImplicitPrelude #-}
```
Or directly in your project `.cabal` file, if you want to use in every module by
default:
```haskell
default-extensions: NoImplicitPrelude
```
Add `relude` as a dependency of your project. Then add the following import to
your modules:
```haskell
import Relude
```
## Difference from Prelude [↑](#structure-of-this-tutorial)
Main differences from `Prelude` can be grouped into the following categories:
* Changed behavior of common functions
+ `head`, `tail`, `last`, `init` work with `NonEmpty a` instead of `[a]`.
* `lines`, `unlines`, `words`, `unwords` work with `Text` instead of `String`.
+ `show` is polymorphic over return type.
+ Functions `sum` and `product` are strict now, which makes them more efficient.
+ You can't call `elem` and `notElem` functions over `Set` and `HashSet`.
These functions are forbidden for these two types because of the performance
reasons.
+ `error` takes `Text`
+ `undefined` triggers a compiler warning, because you probably don't want to
leave `undefined` in your code. Either use `throwIO`, `Except`, `error` or
`bug`.
* Not reexported
+ `read`
+ `lookup` for lists
+ `log`
* Completely new functions are brougth into scope
+ See [What's new?](#whats-new-) section for a detailed overview.
* New reexports
+ See [Reexports](#reexports-) section for a detailed overview.
## Reexports [↑](#structure-of-this-tutorial)
`relude` reexports some parts of the following libraries:
* [`base`](http://hackage.haskell.org/package/base)
* [`bytestring`](http://hackage.haskell.org/package/bytestring)
* [`containers`](http://hackage.haskell.org/package/containers)
* [`deepseq`](http://hackage.haskell.org/package/deepseq)
* [`hashable`](http://hackage.haskell.org/package/hashable)
* [`mtl`](http://hackage.haskell.org/package/mtl)
* [`stm`](http://hackage.haskell.org/package/stm)
* [`text`](http://hackage.haskell.org/package/text)
* [`transformers`](http://hackage.haskell.org/package/transformers)
* [`unordered-containers`](http://hackage.haskell.org/package/unordered-containers)
If you want to clean up imports after switching to `relude`, you can use
`relude`-specific [`.hlint.yaml`](.hlint.yaml) configuration for this task.
### base
Multiple sorting functions are available:
+ `sortBy :: (a -> a -> Ordering) -> [a] -> [a]`: sorts list using given custom comparator.
+ `sortWith :: Ord b => (a -> b) -> [a] -> [a]`: sorts a list based on some property of its elements.
+ `sortOn :: Ord b => (a -> b) -> [a] -> [a]`: just like `sortWith`, but more
time-efficient if function is calculated slowly (though less
space-efficient). So you should write `sortOn length` (would sort elements
by length) but `sortWith fst` (would sort list of pairs by first element).
`readMaybe` and `readEither` are like `read` but total and give either `Maybe`
or `Either` with parse error.
`(&)` – reverse application. `x & f & g` instead of `g $ f $ x` is useful sometimes.
Some generally useful modules from `base` package, like: `Control.Applicative`,
`Data.Traversable`, `Data.Monoid`, `Data.List`, and lots of others.
`liftIO` and `MonadIO` are exported by default. A lot of `IO` functions are
generalized to `MonadIO`.
[`Bifunctor`](http://hackage.haskell.org/package/base-4.9.1.0/docs/Data-Bifunctor.html)
type class with useful instances is exported.
* `first` and `second` functions apply a function to first/second part of a tuple (for tuples).
* `bimap` takes two functions and applies them to first and second parts respectively.
`trace`, `traceM`, `traceShow`, etc. are available by default. GHC will warn you
if you accidentally leave them in code, however (same for `undefined`).
We also have `data Undefined = Undefined` (which, too, comes with warnings).
`relude` reexports `Exception` type from the `base` package and introduces the
`bug` function as an alternative to `error`. There's also a very convenient
`Exc` pattern-synonym to handle exceptions of different types.
See [`Relude.Exception`](src/Relude/Exception.hs) module for details on exceptions.
### containers & unordered-containers
The following types from these two packages are exported:
Then, some commonly used types:
* Maps: strict versions of `Map`, `HashMap`, `IntMap`.
* Sets: `Set`, `HashSet`, `IntSet`.
* Sequences: `Seq`.
### text & bytestring
`relude` exports `Text` and `ByteString` (as well as synonyms `LText` and `LByteString`
for lazy versions) and some functions work with `Text` instead of `String` –
specifically, IO functions (`readFile`, `putStrLn`, etc) and `show`. In fact, `show`
is polymorphic and can produce strict or lazy `Text`, `String`, or `ByteString`.
Also, `toText/toLText/toString` can convert `Text|LText|String` types to
`Text/LText/String`. If you want to convert to and from `ByteString` use
`encodeUtf8/decodeUtf8` functions.
### transformers & mtl
The following parts of these two libraries are exported:
* Transformers: `State[T]`, `Reader[T]`, `ExceptT`, `MaybeT`.
* Classes: `MonadReader`, `MonadState`, `MonadError`.
### Deepseq
`deepseq` is exported. For instance, if you want to force deep evaluation of
some value (in `IO`), you can write `evaluateNF a`. WHNF evaluation is possible
with `evaluateWHNF a`.
## What's new? [↑](#structure-of-this-tutorial)
Finally, we can move to part describing the new cool features we bring with `relude`.
### Available by default
* Safe analogue for list functions: use `viaNonEmpty` function to get `Maybe a`.
+ `viaNonEmpty head :: [a] -> Maybe a`
* `uncons` splits a list at the first element.
* `ordNub` and `sortNub` are _O(n log n)_ versions of `nub` (which is quadratic)
and `hashNub` and `unstableNub` are almost _O(n)_ versions of `nub`.
* `whenM`, `unlessM`, `ifM`, `guardM` are available and do what you expect
them to do (e.g. `whenM (doesFileExist "foo")`).
* General fold functions:
```haskell
foldMapA :: (Monoid b, Applicative m, Foldable f) => (a -> m b) -> f a -> m b
foldMapM :: (Monoid b, Monad m, Foldable f) => (a -> m b) -> f a -> m b
```
* `when(Just|Nothing|Left|Right|NotEmpty)[M][_]`
let you conditionally execute something. Before:
```haskell
case mbX of
Nothing -> return ()
Just x -> f x
```
After:
```haskell
whenJust mbX $ \x ->
f x
```
* `for_` for loops. There's also `forM_` but `for_` looks a bit nicer.
```haskell
for_ [1..10] $ \i -> do
...
```
* `andM`, `allM`, `anyM`, `orM` are monadic version of corresponding functions from `base`.
* Conversions between `Either` and `Maybe` like `rightToMaybe` and `maybeToLeft`
with clear semantic.
* `using(Reader|State)[T]` functions as aliases for `flip run(Reader|State)[T]`.
* [`One` type class](src/Relude/Container/One.hs)
for creating singleton containers. Even monomorhpic ones like `Text`.
* `evaluateWHNF` and `evaluateNF` functions as clearer and lifted aliases for
`evaluate` and `evaluate . force`.
* `MonadFail` instance for `Either`.
### Need to import explicitly
* Convenient functions to work with `(Bounded a, Enum a)` types:
1. `universe :: (Bounded a, Enum a) => [a]`: get all values of the type.
2. `inverseMap :: (Bounded a, Enum a, Ord k) => (a -> k) -> k -> Maybe a`: convert functions like `show` to parsers.
* Nice helpers to deal with `newtype`s in a more pleasant way:
```haskell
ghci> newtype Foo = Foo Bool deriving Show
ghci> under not (Foo True)
Foo False
```
* Functions to operate with `CallStack`:
```haskell
>>> foo :: HasCallStack => String; foo = ownName
>>> foo
"foo"
```
* `Foldable1` typeclass that contains generalized interface for folding
non-empty structures like `NonEmpty`.
* `Validation` data type as an alternative to `Either` when you want to combine
all errors.
* [`StaticMap` and `DynamicMap` type classes](src/Relude/Extra/Map.hs) as a
general interface for `Map`-like data structures.
Explore `Extra` modules: [`Relude.Extra`](src/Relude/Extra/)
## Migration guide [↑](#structure-of-this-tutorial)
In order to replace default `Prelude` with `relude` you should start with instructions given in
[_get started_](#get-started-) section.
### Code changes
This section describes what you need to change to make your code compile with `relude`.
1. Enable `-XOverloadedStrings` extension by default for your project.
2. Since `head`, `tail`, `last` and `init` work for `NonEmpty` you should
refactor your code in one of the multiple ways described below:
1. Change `[a]` to `NonEmpty a` where it makes sense.
2. Use functions which return `Maybe`. There is the `viaNonEmpty` function for this.
And you can use it like `viaNonEmpty last l`.
+ `tail` is `drop 1`. It's almost never a good idea to use `tail` from `Prelude`.
3. Add `import qualified Relude.Unsafe as Unsafe` and replace function with qualified usage.
3. If you use `fromJust` or `!!` you should use them from `import qualified Relude.Unsafe as Unsafe`.
4. If you use `foldr` or `forM_` or similar for something like `Maybe a` or
`Either a b` it's recommended to replace usages of such function with
monomorhpic alternatives:
* `Maybe`
+ `(?:) :: Maybe a -> a -> a`
+ `fromMaybe :: a -> Maybe a -> a`
+ `maybeToList :: Maybe a -> [a]`
+ `maybeToMonoid :: Monoid m => Maybe m -> m`
+ `maybe :: b -> (a -> b) -> Maybe a -> b`
+ `whenJust :: Applicative f => Maybe a -> (a -> f ()) -> f ()`
+ `whenJustM :: Monad m => m (Maybe a) -> (a -> m ()) -> m ()`
* `Either`
+ `fromLeft :: a -> Either a b -> a`
+ `fromRight :: b -> Either a b -> b`
+ `either :: (a -> c) -> (b -> c) -> Either a b -> c`
+ `whenRight_ :: Applicative f => Either l r -> (r -> f ()) -> f ()`
+ `whenRightM_ :: Monad m => m (Either l r) -> (r -> m ()) -> m ()`
5. Forget about `String` type.
+ Replace `(++)` with `(<>)` for `String`-like types.
+ Use `toText/toLText/toString` functions to convert to `Text/LazyText/String` types.
+ Use `encodeUtf8/decodeUtf8` to convert to/from `ByteString`.
+ Use `(putStr[Ln]|readFile|writeFile|appendFile)[Text|LText|BS|LBS]` functions.
6. Since `show` doesn't come from `Show` anymore, you need to export
`Text.Show` module if you want to implement `Show` instance manually. This can be done like this:
```haskell
import qualified Text.Show
```
7. Run `hlint` using `.hlint.yaml` file from `relude` package to cleanup code and imports.
### Running HLint on CI
Instead of storing a `relude`-specific `.hlint.yaml` file inside your repository,
you can run HLint with this file automatically on any CI service such as
[Travis CI](https://travis-ci.org/) or [Circle CI](https://circleci.com/).
For this you need to:
1. Find the commit hash of the `relude` version you are using (can be found in [releases](https://github.com/kowainik/relude/releases)).
2. Run the command that downloads `.hlint.yaml` for that version.
3. Run `hlint` using this file.
For the latest `relude` version, this can be achieved by executing the following
two commands on your CI:
```yaml
curl https://raw.githubusercontent.com/kowainik/relude/v0.6.0.0/.hlint.yaml -o .hlint-relude.yaml
curl -sSL https://raw.github.com/ndmitchell/neil/master/misc/travis.sh | sh -s -- hlint -h .hlint-relude.yaml .
```
See an example of this feature being used in [Summoner](https://github.com/kowainik/summoner/blob/b6c3ecb7cd9bc8d1451e2cc78cd020cd2e473564/.travis.yml#L58-L59).
## Comparison with other alternative preludes [↑](#structure-of-this-tutorial)
There are quite a few libraries that can be used as alternative preludes in
Haskell, let's compare Relude with some of them.
### Relude vs Protolude [↑](#structure-of-this-tutorial)
[Protolude](https://github.com/sdiehl/protolude) is one of the most popular
alternative preludes. It's also relatively small, but:
1. Protolude supports older GHC versions (from GHC 7.6.1) while `relude` only
supports from GHC 8.0.2. So if you aim ancient GHC versions, `protolude`
might be a better choice. But because of that it contains a lot of CPP, code
is ugly in some places as a consequence and it's more difficult to add,
remove or change things there.
2. `relude` has much better documentation:
* [High-level overview of internal module structure](http://hackage.haskell.org/package/relude/docs/Relude.html)
* 100% Haddock coverage
* Almost every function has usage examples and all examples are tested with
`doctest` (which also sometimes hard to do because of multiple GHC
versions support, but we try really hard)
* [Tutorial + migration guide](#structure-of-this-tutorial) from
`Prelude` and just general description of the whole package and libraries
it depends on.
3. `relude` has custom HLint rules specific to it: you can use them to remove
redundant imports or find hints how to use functions from `relude`. Moreover,
the HLint rules are generated using Dhall and there is [a blog post about
this technique](https://kowainik.github.io/posts/2018-09-09-dhall-to-hlint).
This allows to maintain HLint rules much easier because it's already not an
easy task.
4. `relude` has less dependencies and is slightly lighter because of that but still
very powerful and useful.
5. One minor difference: `head` in `protolude` returns `Maybe a` while in
`relude` it works with `NonEmpty`.
6. Minor feature: `relude` uses type-level magic to forbid `elem` and `notElem`
functions for `Set` and `HashSet` (because `elem` from `Foldable` run in
_O(n)_ time and you can accidentally use `elem` from `Foldable` but with
`relude` you can't).
7. `relude` is opt-in oriented and has a notion of `Extra.*` modules that are
not exported by default from the `Relude` module. So we don't spoil global
namespace but still have a lot of useful features like polymorphic functions
to work with every `newtype`, `Enum/Bounded`-related useful utilities,
functions to take a name of any type as `Text` and much more. It's very easy
to make them accessible package-wide with `base-noprelude` trick!
## For Developers [↑](#structure-of-this-tutorial)
### Generating .hlint.yaml
Note, that we are using custom `hlint` setting which are `Relude` specific. To
keep it up to date don't forget to reflect your changes in this file. We are
using `Dhall` to maintain the configurations. To use it follow the steps below.
First time:
```shell
$ cabal new-install dhall-json
```
Dhall 9.0.0 is required, so make sure that the previous command installed
dhall-json >= 1.4.0.
To generate `hlint` file:
```shell
$ dhall-to-yaml --omitNull <<< './hlint/hlint.dhall' > .hlint.yaml
```
Check that you have generated valid `.hlint.yaml` file without parse errors:
```shell
$ hlint test/Spec.hs
```
See our blog post where we describe the details of the implementation for this solution:
* [Dhall To HLint](https://kowainik.github.io/posts/2018-09-09-dhall-to-hlint)