mirror of
https://github.com/kowainik/relude.git
synced 2024-10-05 16:57:30 +03:00
* [#5] Improve documentation * Update README.md * Add cabal description
This commit is contained in:
parent
2cf5629897
commit
88d055126d
1
.ghci
1
.ghci
@ -1,3 +1,4 @@
|
||||
:set -XNoImplicitPrelude
|
||||
:set -XOverloadedStrings
|
||||
:set -XTypeApplications
|
||||
import Relude
|
||||
|
14
.hlint.yaml
14
.hlint.yaml
@ -103,14 +103,6 @@
|
||||
|
||||
- hint: {lhs: fmap (fmap f) x, rhs: f <<$>> x}
|
||||
|
||||
- warn: {lhs: fmap concat (mapM f s), rhs: Relude.concatMapM f s}
|
||||
- warn: {lhs: concat <$> mapM f s, rhs: Relude.concatMapM f s}
|
||||
|
||||
- warn: {lhs: fmap concat (forM f s), rhs: Relude.concatForM s f}
|
||||
- warn: {lhs: fmap concat (for f s), rhs: Relude.concatForM s f}
|
||||
- warn: {lhs: concat <$> forM f s, rhs: Relude.concatForM s f}
|
||||
- warn: {lhs: concat <$> for f s, rhs: Relude.concatForM s f}
|
||||
|
||||
- hint: { lhs: fmap and (sequence s), rhs: Relude.andM s
|
||||
, note: "Applying this hint would mean that some actions\n that were being executed previously would no longer be executed." }
|
||||
- hint: { lhs: and <$> sequence s, rhs: Relude.andM s
|
||||
@ -389,10 +381,6 @@
|
||||
- warn: { name: "Use 'comparing' from Relude"
|
||||
, lhs: Data.Ord.comparing, rhs: Relude.comparing }
|
||||
|
||||
- warn: { name: "Use 'fmapDefault' from Relude"
|
||||
, lhs: Data.Traversable.fmapDefault, rhs: Relude.fmapDefault }
|
||||
- warn: { name: "Use 'foldMapDefault' from Relude"
|
||||
, lhs: Data.Traversable.foldMapDefault, rhs: Relude.foldMapDefault }
|
||||
- warn: { name: "Use 'forM' from Relude"
|
||||
, lhs: Data.Traversable.forM, rhs: Relude.forM }
|
||||
- warn: { name: "Use 'mapAccumL' from Relude"
|
||||
@ -468,6 +456,8 @@
|
||||
|
||||
- warn: { name: "Use 'Coercible' from Relude"
|
||||
, lhs: GHC.Types.Coercible, rhs: Relude.Coercible }
|
||||
- warn: { name: "Use 'coerce' from Relude"
|
||||
, lhs: Data.Coerce.coerce, rhs: Relude.coerce }
|
||||
|
||||
- warn: { name: "Use 'getStackTrace' from Relude"
|
||||
, lhs: GHC.ExecutionStack.getStackTrace, rhs: Relude.getStackTrace }
|
||||
|
@ -1,7 +1,6 @@
|
||||
sudo: true
|
||||
language: haskell
|
||||
|
||||
|
||||
git:
|
||||
depth: 5
|
||||
|
||||
@ -13,7 +12,6 @@ cache:
|
||||
- "$TRAVIS_BUILD_DIR/.stack-work"
|
||||
|
||||
matrix:
|
||||
|
||||
include:
|
||||
|
||||
- ghc: 8.0.2
|
||||
|
24
CHANGELOG.md
24
CHANGELOG.md
@ -4,23 +4,15 @@ Change log
|
||||
0.1.0
|
||||
=====
|
||||
|
||||
* [#8](https://github.com/kowainik/relude/issues/8):
|
||||
Introduce `StaticMap` and `DynamicMap` type classes as universal interface for
|
||||
Map-like structures.
|
||||
* [#14](https://github.com/kowainik/relude/issues/14):
|
||||
Add `Relude.Extra.*` modules which are not exported by default but have useful
|
||||
functions.
|
||||
* [#7](https://github.com/kowainik/relude/issues/7):
|
||||
Remove `Container.Class.Container`. Export `Foldable`.
|
||||
* [#2](https://github.com/kowainik/relude/issues/2):
|
||||
Remove `microlens` from dependencies.
|
||||
* [#12](https://github.com/kowainik/relude/issues/12):
|
||||
Remove `liquid-haskell` support.
|
||||
* [#10](https://github.com/kowainik/relude/issues/10):
|
||||
Remove `VarArg` module.
|
||||
* [#9](https://github.com/kowainik/relude/issues/9):
|
||||
Remove `safe-exceptions` from dependencies. Reexport `Exception` and
|
||||
`SomeException` from `Control.Exception` instead.
|
||||
* [#7](https://github.com/kowainik/relude/issues/7):
|
||||
Remove `Container.Class.Container`. Export `Foldable`.
|
||||
* [#11](https://github.com/kowainik/relude/issues/11):
|
||||
Remove `TypeOps` module and `type-operators` dependency.
|
||||
* [#13](https://github.com/kowainik/relude/issues/13):
|
||||
@ -35,14 +27,22 @@ Change log
|
||||
* [#18](https://github.com/kowainik/relude/issues/18):
|
||||
Add `LazyStrict` type class for conversions.
|
||||
* `map` is not `fmap` anymore. Reexport `map` from `Data.List`
|
||||
* [#17](https://github.com/kowainik/relude/issues/17):
|
||||
Add `foldMapA` and `foldMapM` functions.
|
||||
* [#12](https://github.com/kowainik/relude/issues/12):
|
||||
Remove `liquid-haskell` support.
|
||||
* [#20](https://github.com/kowainik/relude/issues/20):
|
||||
Add `viaNonEmpty` function.
|
||||
* [#21](https://github.com/kowainik/relude/issues/21):
|
||||
Add `MonadFail` instance for `Either`.
|
||||
* [#17](https://github.com/kowainik/relude/issues/17):
|
||||
Add `foldMapA` and `foldMapM` functions.
|
||||
* [#4](https://github.com/kowainik/relude/issues/4):
|
||||
Rename package to `Relude`.
|
||||
* [#14](https://github.com/kowainik/relude/issues/14):
|
||||
Add `Relude.Extra.*` modules which are not exported by default but have useful
|
||||
functions.
|
||||
* [#8](https://github.com/kowainik/relude/issues/8):
|
||||
Introduce `StaticMap` and `DynamicMap` type classes as universal interface for
|
||||
Map-like structures.
|
||||
|
||||
`relude` uses [PVP Versioning][1].
|
||||
The change log is available [on GitHub][2].
|
||||
|
@ -2,20 +2,22 @@
|
||||
|
||||
## :wave: Greetings Traveler!
|
||||
|
||||
I'm glad you're reading this, I really appreciate the effort you're
|
||||
We're glad you're reading this, we really appreciate the effort you're
|
||||
putting in. Thank you for your help in making this library awesome! :sparkles:
|
||||
|
||||
### How to contribute
|
||||
|
||||
#### Report bugs or feature request
|
||||
|
||||
If you have found any bugs or have proposals on how to make this project better,
|
||||
don't hesitate to create issues
|
||||
[here](https://github.com/kowainik/relude/issues/new) in free format.
|
||||
|
||||
#### Create a PR
|
||||
|
||||
We love receiving pull requests from everyone. But, please, don't create a PR
|
||||
without a corresponding issue. It's always better to discuss your future
|
||||
work first. Even if such an issue exist it's still better to express your willing
|
||||
work first. Even if such an issue exists it's still better to express your willing
|
||||
to do that issue under comment section. Thus you will show that you're doing
|
||||
that issue, and nobody else will accidentally do it in parallel with you. Furthermore you
|
||||
also can discuss the best way to implement that issue!
|
||||
@ -27,9 +29,11 @@ To get started with this you should first fork, then clone the repo:
|
||||
Make your changes and consider the following check list to go through before submitting your pull request.
|
||||
|
||||
#### :white_check_mark: Check list
|
||||
|
||||
- [ ] Project compiles
|
||||
- [ ] New/fixed features work as expected
|
||||
- [ ] Old features do not break after the change
|
||||
- [ ] `stylish-haskell` with config in this repo root was used to format code
|
||||
- [ ] _Recommended:_ Commit messages are in the proper format. If the commit
|
||||
addresses an issue start the first line of the commit with the issue number in
|
||||
square parentheses.
|
||||
|
221
README.md
221
README.md
@ -7,18 +7,44 @@ Relude
|
||||
[![Stackage Nightly](http://stackage.org/package/relude/badge/nightly)](http://stackage.org/nightly/package/relude)
|
||||
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
|
||||
|
||||
`relude` is a custom prelude that has:
|
||||
`relude` is a custom prelude based on `universum`. `relude` tries to achieve the following goals:
|
||||
|
||||
1. **Excellent documentation**: tutorial, migration guide from `Prelude`,
|
||||
Haddock with examples for (almost) every function,
|
||||
all examples are tested with [`doctest`](http://hackage.haskell.org/package/doctest),
|
||||
documenation regarding internal module structure.
|
||||
2. `relude`-specific [HLint](http://hackage.haskell.org/package/hlint) rules:
|
||||
[`.hlint.yaml`](https://github.com/kowainik/relude/blob/master/.hlint.yaml)
|
||||
3. Focus on safety, convenience and efficiency.
|
||||
|
||||
What is this file about?
|
||||
------------------------
|
||||
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 unrepresantable. And if it's
|
||||
possible to express this concept through the types then we will 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.
|
||||
5. **Convenience** (like lifted to `MonadIO` functions, more reexports). But we
|
||||
want to bring common types and functions (like `containers` and `bytestrng`)
|
||||
into scope because they are used in almost every application anyways.
|
||||
6. **Provide excellent documentation.**
|
||||
* Tutorial
|
||||
* Migration guide from `Prelude`
|
||||
* Haddock with examples for (almost) every function
|
||||
(all examples are tested with [`doctest`](http://hackage.haskell.org/package/doctest))
|
||||
* Documentation regarding [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.
|
||||
|
||||
This README contains introduction to `Relude` and a tutorial on how to use it.
|
||||
|
||||
@ -27,79 +53,44 @@ Structure of this tutorial
|
||||
|
||||
This tutorial has several parts:
|
||||
|
||||
1. [Philosophy and motivation.](#why-another-custom-prelude-)
|
||||
2. [How to use `relude`.](#how-to-use-relude-)
|
||||
3. [Changes in `Prelude` (some gotchas).](#gotchas-)
|
||||
4. [Already known things that weren't in `Prelude` brought into scope.](#things-that-you-were-already-using-but-now-you-dont-have-to-import-them-explicitly-)
|
||||
5. [New things added.](#whats-new-)
|
||||
6. [Migration guide from `Prelude`.](#migration-guide-from-prelude-)
|
||||
1. [Motivation.](#motivation-)
|
||||
2. [Get started.](#get-started-)
|
||||
3. [Difference from `Prelude`.](#difference-from-prelude-)
|
||||
4. [Reexports.](#reexports-)
|
||||
5. [What's new?](#whats-new-)
|
||||
6. [Migration guide.](#migration-guide-)
|
||||
|
||||
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).
|
||||
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).
|
||||
|
||||
Why another custom Prelude? [↑](#structure-of-this-tutorial)
|
||||
---------------------------
|
||||
Motivation [↑](#structure-of-this-tutorial)
|
||||
------------------------------------------
|
||||
|
||||
### Motivation
|
||||
We decided to base `relude` on `universum` due to the following reasons:
|
||||
|
||||
We strive to be as productive as possible. That's why we are using [_Haskell_](https://haskell-lang.org/). This choice of language implies
|
||||
that we're restricted to use [`Prelude`](http://hackage.haskell.org/package/base-4.9.1.0/docs/Prelude.html):
|
||||
implicit import of basic functions, type classes and data types. Unfortunately, the default `Prelude`
|
||||
[is considered to be not so good](https://news.ycombinator.com/item?id=8002749)
|
||||
due to some historical reasons.
|
||||
1. `universum` helps to achieve our goals more than any other custom prelude.
|
||||
2. We worked on `universum` a lot (just check contributors statistics) and we
|
||||
know its internal structure.
|
||||
|
||||
This is why we decided to use a better tool. Luckily, _Haskell_ provides us with the ability
|
||||
to replace default `Prelude` with an alternative. All we had to do is to implement a
|
||||
new basic set of defaults. There already were plenty of [preludes](https://guide.aelve.com/haskell/alternative-preludes-zr69k1hc),
|
||||
so we didn't plan to implement everything from scratch.
|
||||
After some long, hot discussions, our team decided to base our custom prelude on
|
||||
[`protolude`](https://github.com/sdiehl/protolude). If you're not familiar with it,
|
||||
you can read [a tutorial about `protolude`](http://www.stephendiehl.com/posts/protolude.html).
|
||||
The motivation to create another alternative prelude instead of modifying
|
||||
existing one is that it's hard to change preludes in any way. `relude`
|
||||
uses approach with `Extra.*` modules which are not exported by default so it's
|
||||
quite easy to bring something new (that satisfies `relude` goals) and let users
|
||||
decide to use it or not.
|
||||
|
||||
The next section explains why we've made this choice and what we are willing to do.
|
||||
This tutorial doesn't cover the differences from `protolude`. Instead, it explains how Relude is different from regular `Prelude`.
|
||||
Unlike `universum`, we are:
|
||||
|
||||
### Main goals
|
||||
1. Not trying to replace `Foldable` with custom `Container` type class. We only
|
||||
forbid `elem` and `notElem` functions for sets due to performance reasons.
|
||||
2. Have less dependencies: no `vector`, no `microlens`, no `safe-exceptions`, no `type-operators`.
|
||||
3. Have a lot of other different improvements.
|
||||
|
||||
While creating and maintaining a custom prelude, we are pursuing the following goals:
|
||||
Get started [↑](#structure-of-this-tutorial)
|
||||
--------------------------------------------
|
||||
|
||||
1. Avoid all [partial functions](https://www.reddit.com/r/haskell/comments/5n51u3/why_are_partial_functions_as_in_head_tail_bad/).
|
||||
We like [total](http://mathworld.wolfram.com/TotalFunction.html) and exception-free functions.
|
||||
You can still use some unsafe functions from `Relude.Unsafe` module,
|
||||
but they are not exported by default.
|
||||
2. Use more efficient [string representations](https://www.reddit.com/r/haskell/comments/29jw0s/whats_wrong_with_string/).
|
||||
`String` type is crushingly inefficient. All our functions either try to be polymorphic over string
|
||||
type or use [`Text`](http://hackage.haskell.org/package/text-1.2.2.1/docs/Data-Text.html)
|
||||
as the default string type. Because the community is evolving slowly, some libraries still use `String` type, so `String` type alias is still reexported. We recommend to avoid `String` as much as you can!
|
||||
3. Try to not reinvent the wheel. We're not trying to rebuild whole type hierarchy from scratch,
|
||||
as it's done in [`classy-prelude`](https://github.com/snoyberg/mono-traversable).
|
||||
Instead, we reexport common and well-known things from `base` and some other
|
||||
libraries that are used in everyday production programming in _Haskell_.
|
||||
4. Export more useful and commonly used functions. [Hello, my name is Dmitry. I was
|
||||
coding _Haskell_ for 3 years but still hoogling which module `liftIO` comes from.](https://twitter.com/magnars/status/834683466130345984)
|
||||
Things like `liftIO`, `ReaderT` type, `MVar`-related functions have unambiguous names,
|
||||
are used in almost every non-trivial project, and it's really tedious to import them
|
||||
manually every time.
|
||||
|
||||
Unlike `protolude`, we are:
|
||||
|
||||
1. Not trying to be as general as possible (thus we don't export much from
|
||||
[`GHC.Generics`](https://github.com/sdiehl/protolude/blob/41710698eedc66fb0bfc5623d3c3a672421fbab5/src/Protolude.hs#L365)).
|
||||
2. Not trying to maintain every version of `ghc` compiler (only the latest 3)
|
||||
3. Trying to make writing production code easier (see
|
||||
[enhancements and fixes](https://github.com/kowainik/relude/issues)).
|
||||
|
||||
How to use Relude [↑](#structure-of-this-tutorial)
|
||||
--------------------
|
||||
|
||||
Okay, enough philosophy. If you want to just start using `relude` and
|
||||
explore it with the help of compiler, set everything up according to the instructions below.
|
||||
|
||||
If you want to get familiar with `relude` internal structure, you can just
|
||||
read top-level documentation for
|
||||
[`Relude`](http://hackage.haskell.org/package/relude/docs/Relude.html)
|
||||
module.
|
||||
If you want to start using `relude` in your project and explore it with the help
|
||||
of compiler, set everything up according to the instructions below.
|
||||
|
||||
### `base-noprelude`
|
||||
|
||||
@ -108,8 +99,8 @@ the following steps:
|
||||
|
||||
1. Replace `base` dependency with corresponding version of `base-noprelude` in
|
||||
your `.cabal` file.
|
||||
2. Add the following `Prelude` module to your project (both file and `exposed-modules`):
|
||||
```haskel
|
||||
2. Add the following `Prelude` module to your project (both to filesystem and to `exposed-modules`):
|
||||
```haskell
|
||||
module Prelude
|
||||
( module Relude
|
||||
) where
|
||||
@ -144,43 +135,51 @@ Then add the following import to your modules:
|
||||
import Relude
|
||||
```
|
||||
|
||||
Gotchas [↑](#structure-of-this-tutorial)
|
||||
-------
|
||||
Difference from Prelude [↑](#structure-of-this-tutorial)
|
||||
--------------------------------------------------------
|
||||
|
||||
* `head`, `tail`, `last`, `init` work with `NonEmpty a` instead of `[a]`.
|
||||
* Safe analogue for `head` function: `safeHead :: [a] -> Maybe a` or you can
|
||||
use our `viaNonEmpty` function to get `Maybe a`: `viaNonEmpty head :: [a] -> Maybe a`.
|
||||
* `undefined` triggers a compiler warning, which is probably not what you want. Either use `throwIO`, `Except`, `error` or `bug`.
|
||||
* `undefined` triggers a compiler warning, because you probably don't want to
|
||||
leave `undefined` in your code. Either use `throwIO`, `Except`, `error` or
|
||||
`bug`.
|
||||
* Multiple sorting functions are available without imports:
|
||||
+ `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).
|
||||
+ `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).
|
||||
* Functions `sum` and `product` are strict now, which makes them more efficient.
|
||||
* If you try to do something like `putStrLn "hi"`, you'll get an error message if
|
||||
`OverloadedStrings` is enabled – it happens because the compiler doesn't know what
|
||||
type to infer for the string. Use `putTextLn` in this case.
|
||||
* Since `show` doesn't come from `Show` anymore, you can't write `Show` instances easily.
|
||||
* Since `show` doesn't come from `Show` anymore, you need to export `Show` from
|
||||
`Text.Show` module if you want to implement `Show` instance manually.
|
||||
* You can't call `elem` and `notElem` functions over `Set` and `HashSet`. These
|
||||
functions are forbidden for these two types because of performance reasons.
|
||||
functions are forbidden for these two types because of the performance reasons.
|
||||
* `error` takes `Text`.
|
||||
* `lookup` doesn't work on list of pairs.
|
||||
|
||||
|
||||
Things that you were already using, but now you don't have to import them explicitly [↑](#structure-of-this-tutorial)
|
||||
------------------------------------------------------------------------------------
|
||||
Reexports [↑](#structure-of-this-tutorial)
|
||||
------------------------------------------
|
||||
|
||||
### Commonly used libraries
|
||||
|
||||
First of all, we reexport some generally useful modules: `Control.Applicative`,
|
||||
`Data.Traversable`, `Data.Monoid`, `Control.DeepSeq`, `Data.List`, and lots of others.
|
||||
Just remove unneeded imports after importing `Relude` (GHC should tell you which ones).
|
||||
`Data.Traversable`, `Data.Monoid`, `Control.DeepSeq`, `Data.List`, and lots of
|
||||
others. Just remove unneeded imports after importing `Relude` (you can use
|
||||
`.hlint.yaml` file for this).
|
||||
|
||||
Then, some commonly used types: `Map/HashMap/IntMap`, `Set/HashSet/IntSet`, `Seq`, `Text` and `ByteString`
|
||||
(as well as synonyms `LText` and `LByteString` for lazy versions).
|
||||
Then, some commonly used types: `Map/HashMap/IntMap`, `Set/HashSet/IntSet`,
|
||||
`Seq`, `Text` and `ByteString` (as well as synonyms `LText` and `LByteString`
|
||||
for lazy versions).
|
||||
|
||||
`liftIO` and `MonadIO` are exported by default. A lot of `IO` functions are generalized to `MonadIO`.
|
||||
`liftIO` and `MonadIO` are exported by default. A lot of `IO` functions are
|
||||
generalized to `MonadIO`.
|
||||
|
||||
`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`.
|
||||
`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`.
|
||||
|
||||
We also reexport big chunks of these libraries: `mtl`, `stm`.
|
||||
|
||||
@ -195,7 +194,9 @@ type class with useful instances is exported.
|
||||
We export `Text` and `LText`, 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.
|
||||
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.
|
||||
|
||||
### Debugging and `undefined`s
|
||||
|
||||
@ -209,17 +210,23 @@ We also have `data Undefined = Undefined` (which, too, comes with warnings).
|
||||
TODO: write about reexports, `Bug` and `Exc` pattern.
|
||||
|
||||
What's new? [↑](#structure-of-this-tutorial)
|
||||
-----------
|
||||
--------------------------------------------
|
||||
|
||||
Finally, we can move to part describing the new cool features we bring with `relude`.
|
||||
|
||||
* Safe analogue for `head` function: `safeHead :: [a] -> Maybe a` or you can
|
||||
use our `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`.
|
||||
* `(&)` – reverse application. `x & f & g` instead of `g $ f $ x` is useful sometimes.
|
||||
* `whenM`, `unlessM`, `ifM`, `guardM` are available and do what you expect
|
||||
them to do (e.g. `whenM (doesFileExist "foo")`).
|
||||
* Very generalized version of `concatMapM`, too, is available and does what expected.
|
||||
* 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
|
||||
```
|
||||
* `readMaybe` and `readEither` are like `read` but total and give either
|
||||
`Maybe` or `Either` with parse error.
|
||||
* `when(Just|Nothing|Left|Right|NotEmpty)[M][_]`
|
||||
@ -228,14 +235,14 @@ Finally, we can move to part describing the new cool features we bring with `rel
|
||||
```haskell
|
||||
case mbX of
|
||||
Nothing -> return ()
|
||||
Just x -> ... x ...
|
||||
Just x -> f x
|
||||
```
|
||||
|
||||
After:
|
||||
|
||||
```haskell
|
||||
whenJust mbX $ \x ->
|
||||
... x ...
|
||||
f x
|
||||
```
|
||||
|
||||
* `for_` for loops. There's also `forM_` but `for_` looks a bit nicer.
|
||||
@ -250,21 +257,23 @@ Finally, we can move to part describing the new cool features we bring with `rel
|
||||
* 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](https://github.com/kowainik/relude/blob/master/src/Containers.hs#L473)
|
||||
* [`One` type class](src/Relude/Container/One.hs)
|
||||
for creating singleton containers. Even monomorhpic ones like `Text`.
|
||||
* [`StaticMap` and `DynamicMap`type classes](src/Relude/Extra/Map.hs) as a
|
||||
general interface for `Map`-like data structures.
|
||||
* `evaluateWHNF` and `evaluateNF` functions as clearer and lifted aliases for
|
||||
`evaluate` and `evaluate . force`.
|
||||
* `ToPairs` type class for data types that can be converted to list of pairs (like `Map` or `HashMap` or `IntMap`).
|
||||
* `MonadFail` instance for `Either`.
|
||||
|
||||
Migration guide from Prelude [↑](#structure-of-this-tutorial)
|
||||
----------------------------
|
||||
Migration guide [↑](#structure-of-this-tutorial)
|
||||
------------------------------------------------
|
||||
|
||||
In order to replace default `Prelude` with `relude` you should start with instructions given in
|
||||
[how to use relude](https://github.com/kowainik/relude#how-to-use-relude-) section.
|
||||
[_get started_](#get-started-) section.
|
||||
|
||||
This section describes what you need to change to make your code compile with `relude`.
|
||||
|
||||
1. Enable `-XOverloadedStrings` and `-XTypeFamilies` extension by default for your project.
|
||||
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.
|
||||
|
@ -20,58 +20,56 @@ import qualified Relude.Unsafe as Unsafe
|
||||
|
||||
main :: IO ()
|
||||
main = defaultMain
|
||||
[ bgroupList listOfSmall "small"
|
||||
, bgroupList listOfBig "big"
|
||||
, bgroupList (nStrings 'z') "small str"
|
||||
, bgroupList (nStrings 'c') "big str"
|
||||
, bgroupFold
|
||||
]
|
||||
[ bgroupList listOfSmall "small"
|
||||
, bgroupList listOfBig "big"
|
||||
, bgroupList (nStrings 'z') "small str"
|
||||
, bgroupList (nStrings 'c') "big str"
|
||||
, bgroupFold
|
||||
]
|
||||
|
||||
bgroupList :: forall a .
|
||||
(Ord a, Hashable a, NFData a)
|
||||
bgroupList :: forall a . (Ord a, Hashable a, NFData a)
|
||||
=> (Int -> [a])
|
||||
-> String
|
||||
-> Benchmark
|
||||
bgroupList f name = bgroup name $ map ($ f)
|
||||
[ bgroupNubAll 100
|
||||
, bgroupNubAll 500
|
||||
, bgroupNubAll 1000
|
||||
, bgroupNubHugeList 5000
|
||||
, bgroupNubHugeList 500000
|
||||
, bgroupNubHugeList 1000000
|
||||
]
|
||||
where
|
||||
bgroupNubAll :: Int -> (Int -> [a]) -> Benchmark
|
||||
bgroupNubAll = bgroupNub True
|
||||
[ bgroupNubAll 100
|
||||
, bgroupNubAll 500
|
||||
, bgroupNubAll 1000
|
||||
, bgroupNubHugeList 5000
|
||||
, bgroupNubHugeList 500000
|
||||
, bgroupNubHugeList 1000000
|
||||
]
|
||||
where
|
||||
bgroupNubAll :: Int -> (Int -> [a]) -> Benchmark
|
||||
bgroupNubAll = bgroupNub True
|
||||
|
||||
bgroupNubHugeList :: Int -> (Int -> [a]) -> Benchmark
|
||||
bgroupNubHugeList = bgroupNub False
|
||||
bgroupNubHugeList :: Int -> (Int -> [a]) -> Benchmark
|
||||
bgroupNubHugeList = bgroupNub False
|
||||
|
||||
bgroupNub :: Bool -> Int -> (Int -> [a]) -> Benchmark
|
||||
bgroupNub isNub n listOf =
|
||||
bgroup (show n) nubBenchs
|
||||
where
|
||||
listN :: [a]
|
||||
listN = listOf n
|
||||
bgroupNub :: Bool -> Int -> (Int -> [a]) -> Benchmark
|
||||
bgroupNub isNub n listOf = bgroup (show n) nubBenchs
|
||||
where
|
||||
listN :: [a]
|
||||
listN = listOf n
|
||||
|
||||
nubBenchs :: [Benchmark]
|
||||
nubBenchs =
|
||||
(if isNub
|
||||
then (:) (bench "nub" $ nf nub listN)
|
||||
else id)
|
||||
[ bench "ordNub" $ nf ordNub (listN :: [a])
|
||||
, bench "hashNub" $ nf hashNub (listN :: [a])
|
||||
, bench "sortNub" $ nf sortNub (listN :: [a])
|
||||
, bench "hashSet" $ nf unstableNub (listN :: [a])
|
||||
, bench "groupSort" $ nf groupSort (listN :: [a])
|
||||
, bench "safeSort" $ nf safeSort (listN :: [a])
|
||||
]
|
||||
nubBenchs :: [Benchmark]
|
||||
nubBenchs =
|
||||
(if isNub
|
||||
then (:) (bench "nub" $ nf nub listN)
|
||||
else id)
|
||||
[ bench "ordNub" $ nf ordNub (listN :: [a])
|
||||
, bench "hashNub" $ nf hashNub (listN :: [a])
|
||||
, bench "sortNub" $ nf sortNub (listN :: [a])
|
||||
, bench "hashSet" $ nf unstableNub (listN :: [a])
|
||||
, bench "groupSort" $ nf groupSort (listN :: [a])
|
||||
, bench "safeSort" $ nf safeSort (listN :: [a])
|
||||
]
|
||||
|
||||
groupSort :: [a] -> [a]
|
||||
groupSort = map Unsafe.head . group . sort
|
||||
groupSort :: [a] -> [a]
|
||||
groupSort = map Unsafe.head . group . sort
|
||||
|
||||
safeSort :: [a] -> [a]
|
||||
safeSort = map NonEmpty.head . NonEmpty.group . sort
|
||||
safeSort :: [a] -> [a]
|
||||
safeSort = map NonEmpty.head . NonEmpty.group . sort
|
||||
|
||||
listOfSmall :: Int -> [Int]
|
||||
listOfSmall n = let part = n `div` 100 in concat $ replicate part [1..100]
|
||||
|
48
relude.cabal
48
relude.cabal
@ -1,7 +1,48 @@
|
||||
name: relude
|
||||
version: 0.1.0
|
||||
synopsis: Custom prelude from Kowainik
|
||||
description: See README.md file for more details.
|
||||
description:
|
||||
== Goals
|
||||
.
|
||||
* __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.
|
||||
.
|
||||
* __Type-safety__. We like to make invalid states unrepresantable. And if it's
|
||||
possible to express this concept through the types then we will do it.
|
||||
/Example:/ @ whenNotNull :: Applicative f => [a] -> (NonEmpty a -> f ()) -> f () @
|
||||
.
|
||||
* __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@).
|
||||
.
|
||||
* __Minimalism__ (low number of dependencies). We don't force users of @relude@ to
|
||||
stick to some specific lens or text formatting or logging library.
|
||||
.
|
||||
* __Convenience__ (like lifted to @MonadIO@ functions, more reexports). But we
|
||||
want to bring common types and functions (like @containers@ and @bytestrng@)
|
||||
into scope because they are used in almost every application anyways.
|
||||
.
|
||||
* __Provide excellent documentation.__
|
||||
.
|
||||
1. Tutorial
|
||||
.
|
||||
2. Migration guide from @Prelude@
|
||||
.
|
||||
3. Haddock with examples for (almost) every function
|
||||
(all examples are tested with [`doctest`](http://hackage.haskell.org/package/doctest))
|
||||
.
|
||||
4. Documentation regarding [internal module structure]((http://hackage.haskell.org/package/relude/docs/Relude.html))
|
||||
.
|
||||
5. @relude@-specific [HLint](http://hackage.haskell.org/package/hlint) rules: @[.hlint.yaml](https://github.com/kowainik/relude/blob/master/.hlint.yaml)@
|
||||
.
|
||||
* __User-friendliness.__ Ability to quickly migrate to @relude@ if you're familiar
|
||||
with the common libraries like @text@ and @containers@.
|
||||
.
|
||||
* __Exploration.__ Experiment with new ideas and proposals without introducing
|
||||
breaking changes.
|
||||
|
||||
homepage: https://github.com/kowainik/relude
|
||||
bug-reports: https://github.com/kowainik/relude/issues
|
||||
license: MIT
|
||||
@ -12,7 +53,7 @@ copyright: 2016 Stephen Diehl, 2016-2018 Serokell, 2018 Kowainik
|
||||
category: Prelude
|
||||
stability: stable
|
||||
build-type: Simple
|
||||
cabal-version: >=1.18
|
||||
cabal-version: 1.24
|
||||
tested-with: GHC == 8.0.2
|
||||
, GHC == 8.2.2
|
||||
, GHC == 8.4.3
|
||||
@ -34,7 +75,6 @@ library
|
||||
Relude.Bool.Guard
|
||||
Relude.Bool.Reexport
|
||||
Relude.Container
|
||||
Relude.Container.Map
|
||||
Relude.Container.One
|
||||
Relude.Container.Reexport
|
||||
Relude.Debug
|
||||
@ -71,6 +111,7 @@ library
|
||||
Relude.Extra.Bifunctor
|
||||
Relude.Extra.Enum
|
||||
Relude.Extra.Group
|
||||
Relude.Extra.Map
|
||||
Relude.Unsafe
|
||||
|
||||
|
||||
@ -111,6 +152,7 @@ test-suite relude-test
|
||||
|
||||
ghc-options: -Wall -threaded
|
||||
default-language: Haskell2010
|
||||
default-extensions: NoImplicitPrelude
|
||||
|
||||
test-suite relude-doctest
|
||||
type: exitcode-stdio-1.0
|
||||
|
@ -8,11 +8,9 @@ License: MIT
|
||||
-- | This module exports all container-related stuff.
|
||||
|
||||
module Relude.Container
|
||||
( module Relude.Container.Map
|
||||
, module Relude.Container.One
|
||||
( module Relude.Container.One
|
||||
, module Relude.Container.Reexport
|
||||
) where
|
||||
|
||||
import Relude.Container.Map
|
||||
import Relude.Container.One
|
||||
import Relude.Container.Reexport
|
||||
|
@ -13,6 +13,7 @@ module Relude.Extra.Group
|
||||
) where
|
||||
|
||||
import Relude
|
||||
import Relude.Extra.Map
|
||||
|
||||
import Data.List.NonEmpty ((<|))
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
and 'Map'.
|
||||
-}
|
||||
|
||||
module Relude.Container.Map
|
||||
module Relude.Extra.Map
|
||||
( StaticMap (..)
|
||||
, DynamicMap (..)
|
||||
, (!?)
|
@ -12,7 +12,7 @@ module Relude.Function
|
||||
, identity
|
||||
) where
|
||||
|
||||
import Data.Function (const, fix, flip, id, on, ($), (.))
|
||||
import Data.Function (const, fix, flip, id, on, ($), (&), (.))
|
||||
|
||||
-- | Renamed version of 'Prelude.id'.
|
||||
identity :: a -> a
|
||||
|
@ -7,6 +7,8 @@ License: MIT
|
||||
|
||||
module Main where
|
||||
|
||||
import Relude
|
||||
|
||||
import Test.Tasty (defaultMain)
|
||||
|
||||
import Test.Relude.Property (hedgehogTestTree)
|
||||
|
@ -18,7 +18,6 @@ import Test.Tasty.Hedgehog
|
||||
|
||||
import qualified Data.ByteString as B
|
||||
import qualified Data.ByteString.Lazy as LB
|
||||
import qualified Data.List as List
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.Lazy as LT
|
||||
import qualified Hedgehog.Gen as Gen
|
||||
@ -38,8 +37,7 @@ utfProps = testGroup "utf8 conversion property tests"
|
||||
unicode' :: MonadGen m => m U.Char
|
||||
unicode' = do
|
||||
a <- Gen.unicode
|
||||
-- TODO: cleanup after dropping ghc-7.10.3 support
|
||||
if List.elem a ['\65534', '\65535']
|
||||
if elem a ['\65534', '\65535']
|
||||
then unicode'
|
||||
else return a
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user