• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

src/H25-Jan-2021-7,3944,317

test/H25-Jan-2021-467392

ChangeLog.mdH A D25-Jan-20215.1 KiB154100

LICENSEH A D25-Jan-20211 KiB2117

README.mdH A D25-Jan-202113.9 KiB396319

rio.cabalH A D25-Jan-20213.6 KiB175169

README.md

1# The rio library
2
3*A standard library for Haskell*
4
5![Rio](https://camo.githubusercontent.com/fc162fb0024699c85f00eae769085a5fe528153e/68747470733a2f2f7777772e61687374617469632e636f6d2f70686f746f732f636974792f76692d76363837315f30305f31343030783434322e6a7067)
6
7[![Build Status](https://dev.azure.com/snoyberg/rio/_apis/build/status/commercialhaskell.rio?branchName=master)](https://dev.azure.com/snoyberg/rio/_build/latest?definitionId=6&branchName=master)
8
9The goal of the `rio` library is to make it easier to adopt Haskell
10for writing production software.  It is intended as a cross between:
11
12* Collection of well designed, trusted libraries
13* Useful `Prelude` replacement
14* A set of best practices for writing production quality Haskell code
15
16This repository contains the `rio` library and other related
17libraries, such as `rio-orphans`. There is a [tutorial on how to use
18`rio`](https://haskell.fpcomplete.com/library/rio) available on FP
19Complete's Haskell site. This README discusses project goals and
20collects other reference information.
21
22## Standard library
23
24While GHC ships with a `base` library, as well as a number of other
25common packages like `directory` and `transformers`, there are large
26gaps in functionality provided by these libraries. This choice for a
27more minimalistic `base` is by design, but it leads to some
28unfortunate consequences:
29
30* For a given task, it's often unclear which is the right library to
31  use
32* When writing libraries, there is often concern about adding
33  dependencies to any libraries outside of `base`, due to creating a
34  heavier dependency footprint
35* By avoiding adding dependencies, many libraries end up
36  reimplementing the same functionality, often with incompatible types
37  and type classes, leading to difficulty using libraries together
38
39This library attempts to define a standard library for Haskell. One
40immediate response may be [XKCD #927](https://xkcd.com/927/):
41
42![XKCD Standards](https://imgs.xkcd.com/comics/standards.png)
43
44To counter that effect, this library takes a specific approach: __it
45reuses existing, commonly used libraries__. Instead of defining an
46incompatible `Map` type, for instance, we standardize on the commonly
47used one from the `containers` library and reexport it from this
48library.
49
50This library attempts to define a set of libraries as "standard,"
51meaning they are recommended for use, and should be encouraged as
52dependencies for other libraries. It does this by depending on these
53libraries itself, and reexporting their types and functions for easy
54use.
55
56Beyond the ecosystem effects we hope to achieve, this will hopefully
57make the user story much easier. For a new user or team trying to get
58started, there is an easy library to depend upon for a large
59percentage of common functionality.
60
61See the dependencies of this package to see the list of packages
62considered standard. The primary interfaces of each of these packages
63is exposed from this library via a `RIO.`-prefixed module reexporting
64its interface.
65
66## Prelude replacement
67
68The `RIO` module works as a prelude replacement, providing more
69functionality and types out of the box than the standard prelude (such
70as common data types like `ByteString` and `Text`), as well as
71removing common "gotchas", like partial functions and lazy I/O. The
72guiding principle here is:
73
74* If something is safe to use in general and has no expected naming
75  conflicts, expose it from `RIO`
76* If something should not always be used, or has naming conflicts,
77  expose it from another module in the `RIO.` hierarchy.
78
79## Best practices
80
81Below is a set of best practices we recommend following. You're
82obviously free to take any, all, or none of this. Over time, these
83will probably develop into much more extensive docs. Some of these
84design decisions will be catered to by choices in the `rio` library.
85
86For Haskellers looking for a set of best practices to follow: you've
87come to the right place!
88
89### Import practices
90
91This library is intended to provide a fully loaded set of basic
92functionality. You should:
93
94* Enable the `NoImplicitPrelude` language extension (see below)
95* Add `import RIO` as your replacement prelude in all modules
96* Use the `RIO.`-prefixed modules as necessary, imported using the
97  recommended qualified names in the modules themselves. For example,
98  `import qualified RIO.ByteString as B`. See the module documentation
99  for more information.
100* Infix operators may be imported unqualified, with a separate import
101  line if necessary. For example, `import RIO.Map ((?!), (\\))`. Do
102  this only if your module contains no overlapping infix names,
103  regardless of qualification. For instance, if you are importing both
104  `RIO.Map.\\` and `RIO.List.\\` do not import either one unqualified.
105
106In the future, we may have editor integration or external tooling to
107help with import management.
108
109### Language extensions
110
111Very few projects these days use bare-bones Haskell 98
112or 2010. Instead, almost all codebases enable some set of additional
113language extensions. Below is a list of extensions we recommend as a
114good default, in that these are:
115
116* Well accepted in the community
117* Cause little to no code breakage versus leaving them off
118* Are generally considered safe
119
120Our recommended defaults are:
121
122```
123AutoDeriveTypeable
124BangPatterns
125BinaryLiterals
126ConstraintKinds
127DataKinds
128DefaultSignatures
129DeriveDataTypeable
130DeriveFoldable
131DeriveFunctor
132DeriveGeneric
133DeriveTraversable
134DoAndIfThenElse
135EmptyDataDecls
136ExistentialQuantification
137FlexibleContexts
138FlexibleInstances
139FunctionalDependencies
140GADTs
141GeneralizedNewtypeDeriving
142InstanceSigs
143KindSignatures
144LambdaCase
145MonadFailDesugaring
146MultiParamTypeClasses
147MultiWayIf
148NamedFieldPuns
149NoImplicitPrelude
150OverloadedStrings
151PartialTypeSignatures
152PatternGuards
153PolyKinds
154RankNTypes
155RecordWildCards
156ScopedTypeVariables
157StandaloneDeriving
158TupleSections
159TypeFamilies
160TypeSynonymInstances
161ViewPatterns
162```
163
164Notes on some surprising choices:
165
166* `RecordWildCards` is really up for debate. It's widely used, but
167  rightfully considered by many to be dangerous. Open question about
168  what we do with it.
169* Despite the fact that `OverloadedStrings` can break existing code,
170  we recommend its usage to encourage avoidance of the `String` data
171  type. Also, for new code, the risk of breakage is much lower.
172* `MonadFailDesugaring` helps prevent partial pattern matches in your
173  code, see [#85](https://github.com/commercialhaskell/rio/issues/85)
174
175Due to concerns about tooling usage (see [issue
176#9](https://github.com/commercialhaskell/rio/issues/9)), we recommend
177adding these extensions on-demand in your individual source modules
178instead of including them in your `package.yaml` or `.cabal` files.
179
180There are other language extensions which are perfectly fine to use as
181well, but are not recommended to be turned on by default:
182
183```
184CPP
185TemplateHaskell
186ForeignFunctionInterface
187MagicHash
188UnliftedFFITypes
189TypeOperators
190UnboxedTuples
191PackageImports
192QuasiQuotes
193DeriveAnyClass
194DeriveLift
195StaticPointers
196```
197
198### GHC Options
199
200We recommend using these GHC complier warning flags on all projects, to catch
201problems that might otherwise go overlooked:
202
203* `-Wall`
204* `-Wcompat`
205* `-Widentities`
206* `-Wincomplete-record-updates`
207* `-Wincomplete-uni-patterns`
208* `-Wpartial-fields`
209* `-Wredundant-constraints`
210
211You may add them per file, or to your `package.yaml`, or pass them on
212the command line when running ghc. We include these in the project
213template's `package.yaml` file.
214
215For code targeting production use, you should also use the flag that turns all
216warnings into errors, to force you to resolve the warnings before you ship your
217code:
218
219* `-Werror`
220
221Further reading:
222
223* Alexis King explains why these are a good idea in [her blog
224post](https://lexi-lambda.github.io/blog/2018/02/10/an-opinionated-guide-to-haskell-in-2018/)
225which was the original inspiration for this section.
226* Max Tagher gives an in-depth overview of these flags, and more,
227[in his blog post](https://medium.com/mercury-bank/enable-all-the-warnings-a0517bc081c3).
228
229### Monads
230
231A primary design choice you'll need to make in your code is how to
232structure your monads. There are many options out there, with various
233trade-offs. Instead of going through all of the debates, we're going
234to point to
235[an existing blog post](https://www.fpcomplete.com/blog/2017/07/the-rio-monad),
236and here just give recommendations.
237
238* If your code is going to perform I/O: it should live in the `RIO`
239  monad. `RIO` is "reader IO." It's the same as `ReaderT env IO`, but
240  includes some helper functions in this library and leads to nicer
241  type signatures and error messages.
242
243* If you need to provide access to specific data to a function, do it
244  via a typeclass constraint on the `env`, _not_ via a concrete
245  env. For example, this is bad:
246
247  ```haskell
248  myFunction :: RIO Config Foo
249  ```
250
251  This is good:
252
253  ```haskell
254  class HasConfig env where
255    configL :: Lens' env Config -- more on this in a moment
256  myFunction :: HasConfig env => RIO env Foo
257  ```
258
259  Reason: by using typeclass constraints on the environment, we can
260  easily compose multiple functions together and collect up the
261  constraints, which wouldn't be possible with concrete
262  environments. We _could_ go more general with mtl-style typeclasses,
263  like `MonadReader` or `MonadHasConfig`, but `RIO` is a perfect
264  balance point in the composability/concreteness space (see blog post
265  above for more details).
266
267* When defining `Has`-style typeclasses for the environments, we use
268  lenses (which are exposed by `RIO`) because it provides for easy
269  composability. We also leverage superclasses wherever possible. As
270  an example of how this works in practice:
271
272  ```haskell
273  -- Defined in RIO.Logger
274  class HasLogFunc env where
275    logFuncL :: Lens' env LogFunc
276
277  class HasConfig env where
278    configL :: Lens' env Config
279  instance HasConfig Config where
280    configL = id
281
282  data Env = Env { envLogFunc :: !LogFunc, envConfig :: !Config }
283  class (HasLogFunc env, HasConfig env) => HasEnv env where
284    envL :: Lens' env Env
285  instance HasLogFunc Env where
286    logFuncL = lens envLogFunc (\x y -> x { envLogFunc = y })
287  instance HasConfig Env where
288    configL = lens envConfig (\x y -> x { envConfig = y })
289  instance HasEnv Env where
290    envL = id
291
292  -- And then, at some other part of the code
293  data SuperEnv = SuperEnv { seEnv :: !Env, seOtherStuff :: !OtherStuff }
294  instance HasLogFunc SuperEnv where
295    logFuncL = envL.logFuncL
296  instance HasConfig SuperEnv where
297    configL = envL.configL
298  instance HasEnv SuperEnv where
299    envL = lens seEnv (\x y -> x { seEnv = y })
300  ```
301
302* If you're writing code that you want to be usable outside of `RIO`
303  for some reason, you should stick to the good mtl-style typeclasses:
304  `MonadReader`, `MonadIO`, `MonadUnliftIO`, `MonadThrow`, and
305  `PrimMonad`. It's better to use `MonadReader`+`Has` than to create
306  new typeclasses like `MonadLogger`, though usually just sticking
307  with the simpler `RIO env` is fine (and can easily be converted to
308  the more general form with `liftRIO`). You should avoid using the
309  following typeclasses (intentionally not exposed from this library):
310  `MonadBase`, `MonadBaseControl`, `MonadCatch`, and `MonadMask`.
311
312### Exceptions
313
314For in-depth discussion, see [safe exception
315handling](https://haskell.fpcomplete.com/tutorial/exceptions). The
316basic idea is:
317
318* If something can fail, and you want people to deal with that failure
319  every time (e.g., `lookup`), then return a `Maybe` or `Either`
320  value.
321* If the user will usually not want to deal with it, then use
322  exceptions. In the case of pure code, use a `MonadThrow`
323  constraint. In the case of `IO` code: use runtime exceptions via
324  `throwIO` (works in the `RIO` monad too).
325* You'll be upset and frustrated that you don't know exactly how some
326  `IO` action can fail. Accept that pain, live with it, internalize
327  it, use `tryAny`, and move on. It's the price we pay for async
328  exceptions.
329* Do all resource allocations with functions like `bracket` and
330  `finally`.
331
332It’s a good idea to define an app-wide exception type:
333
334```haskell
335data AppExceptions
336  = NetworkChangeError Text
337  | FilePathError FilePath
338  | ImpossibleError
339  deriving (Typeable)
340
341instance Exception AppExceptions
342
343instance Show AppExceptions where
344  show =
345    \case
346      NetworkChangeError err -> "network error: " <> (unpack err)
347      FilePathError fp -> "error accessing filepath at: " <> fp
348      ImpossibleError -> "this codepath should never have been executed. Please report a bug."
349```
350
351### Strict data fields
352
353Make data fields strict by default, unless you have a good reason to
354do otherwise.
355
356### Project template
357
358We provide a project template which sets up lots of things for you out
359of the box. You can use it by running:
360
361```
362$ stack new projectname rio
363```
364
365### Safety first
366
367This library intentionally puts safety first, and therefore avoids
368promoting partial functions and lazy I/O. If you think you need lazy
369I/O: you need a streaming data library like conduit instead.
370
371### When to generalize
372
373A common question in Haskell code is when should you generalize. Here
374are some simple guidelines. For parametric polymorphism: _almost
375always_ generalize, it makes your type signatures more informative and
376functions more useful. In other words, `reverse :: [a] -> [a]` is far
377better than `reverse :: [Int] -> [Int]`.
378
379When it comes to typeclasses: the story is more nuanced. For
380typeclasses provided by `RIO`, like `Foldable` or `Traversable`, it's
381generally a good thing to generalize to them when possible. The real
382question is defining your own typeclasses. As a general rule: avoid
383doing so as long as possible. And _if_ you define a typeclass: make
384sure its usage can't lead to accidental bugs by allowing you to swap
385in types you didn't expect.
386
387### Module hierarchy
388
389The `RIO.Prelude.` module hierarchy contains identifiers which are reexported
390by the `RIO` module. The reason for this is to make it easier to view the
391generated Haddocks. The `RIO` module itself is intended to be imported
392unqualified, with `NoImplicitPrelude` enabled. All other modules are _not_
393reexported by the `RIO` module,
394and will document inside of them whether they should be imported qualified or
395unqualified.
396