railroad-0.3.0.0: railroad.cabal
cabal-version: 3.0
name: railroad
version: 0.3.0.0
license: BSD-3-Clause
license-file: LICENSE
author: Frederik Kallstrup Mastratisi
maintainer: mastratisi@proton.me
category: Control, Error Handling
build-type: Simple
tested-with: GHC == 9.10.3, GHC == 9.12.4, GHC == 9.14.1
extra-doc-files: CHANGELOG.md, readme.md
synopsis: Railway-oriented error handling: unwrap, throw, recover or fall back
description:
Railway-oriented error handling for Haskell: one short operator per
step, and the failure paths stop taking up lines.
Each operator looks at a result (@Maybe@, @Either@, @Bool@,
@Validation@, or a collection of them), decides whether it is a failure,
and says what to do about it. Where you would write
> rows <- runQuery (userById uid) >>= either (const (throwError err503)) pure
> user <- case rows of
> [] -> throwError err404
> [u] -> pure u
> _ -> throwError err500
you write
> user <- runQuery (userById uid) ? err503 ?! cardinalityErr err404 (const err500)
Layers are unwrapped left to right: @? err503@ unwraps the database
@Either@, then @?!@ demands exactly one row (none is a 404, several a 500).
What to do about a failure:
* Throw: @?@ (constant error) and @??@ (error computed from the failure).
* Recover: @?~@ and @??~@.
* Observe, then carry on: @?\<*@, as in @runQuery q ?\<* logDbError ? err503@.
* Fall back to another source: @?|@, or @?|<>@ to combine the errors.
What counts as a failure:
* @Nothing@, @Left@, @False@ and @Failure@. A collection fails if any
element does, so an empty collection passes. @Validation@ failures
accumulate, so a whole request can report every problem at once.
* Collections are also checked by size: @?+@ (at least one), @?!@ (exactly
one), @?∅@ (none), and @?∃@ (at least one element succeeded; keeps the
successes).
* Classifiers: @?>@ tags by a predicate. Any function into @Either@,
@Maybe@, @Bool@ or @Validation@ is a classifier, applied with @\<&\>@.
"Railroad.Bifurcate" provides @neg@, @atLeastOne@, @exactlyOne@,
@noneAtAll@ and @anySucceeds@.
Works in any @MonadError@ ("Railroad.MonadError": @mtl@, @ExceptT@,
servant's @Handler@) and in @effectful@ with the @Error@ effect
("Railroad"). All operators are @infixl 1@ like @>>=@ and @\<&\>@, so
they chain without parentheses. The readme has more examples.
homepage: https://github.com/mastratisi/railroad
bug-reports: https://github.com/mastratisi/railroad/issues
source-repository head
type: git
location: https://github.com/mastratisi/railroad
common stuff
ghc-options: -Wall
build-depends:
base >=4.17 && < 4.23,
effectful >= 2.5 && < 2.8,
mtl >= 2.2 && < 2.4,
validation (>= 1.1 && < 1.3) || (>= 1.3.1 && < 1.4)
library
import: stuff
exposed-modules: Railroad
Railroad.Bifurcate
Railroad.MonadError
hs-source-dirs: src
default-language: GHC2021
test-suite railroad-test
import: stuff
default-language: GHC2021
type: exitcode-stdio-1.0
hs-source-dirs: test
main-is: Main.hs
other-modules: Model
RailroadSpec
Railroad.BifurcateSpec
Railroad.MonadErrorSpec
build-tool-depends:
hspec-discover:hspec-discover >= 2.11.14 && < 2.12
build-depends:
base >=4.17 && < 4.23,
hspec >= 2.11.14 && < 2.12,
QuickCheck >= 2.14 && < 2.16,
railroad