cabal-version: 3.0 name: railroad version: 0.3.0.0 x-revision: 1 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.20, railroad