railroad 0.1.1.3 → 0.1.1.4
raw patch · 5 files changed
+33/−24 lines, 5 filesdep ~effectfuldep ~validationPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: effectful, validation
API changes (from Hackage documentation)
Files
- CHANGELOG.md +7/−0
- railroad.cabal +5/−3
- src/Railroad.hs +5/−4
- test/Railroad/MonadErrorSpec.hs +6/−7
- test/RailroadSpec.hs +10/−10
CHANGELOG.md view
@@ -1,5 +1,12 @@ # Revision history for railroad +## 0.1.1.4 -- 2026-10-10+* Widen dependency upper bounds (tests pass):+ - `effectful < 2.8`+ - `validation < 1.4`+* Stop using `Data.Validation.toEither`, which `validation-1.3` no longer exports.+* Declare `hspec-discover` as a `build-tool-depends` of the test suite, so the tests build outside the nix shell.+ ## 0.1.1.3 -- 2026-10-10 * Documentation fixes: correct "success" typo and the doc comment of `(?)` in `Railroad.MonadError`.
railroad.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: railroad-version: 0.1.1.3+version: 0.1.1.4 license: BSD-3-Clause license-file: LICENSE author: Frederik Kallstrup Mastratisi@@ -22,9 +22,9 @@ ghc-options: -Wall build-depends: base >=4.17 && < 4.23,- effectful >= 2.5 && < 2.7,+ effectful >= 2.5 && < 2.8, mtl >= 2.2 && < 2.4,- validation >= 1.1 && < 1.2+ validation >= 1.1 && < 1.4 library import: stuff@@ -42,6 +42,8 @@ main-is: Main.hs other-modules: RailroadSpec 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,
src/Railroad.hs view
@@ -9,7 +9,7 @@ import Data.Bool (bool) import Data.Foldable (toList) import Data.Kind (Type)-import Data.Validation (Validation, toEither)+import Data.Validation (Validation (..)) import Effectful (Eff, type (:>)) import Effectful.Error.Dynamic (Error, throwError_) @@ -40,7 +40,9 @@ bifurcate = id -- either :: (e -> b) -> (a -> b) -> Either e a -> b instance Bifurcate (Validation e a) where- bifurcate = toEither+ bifurcate = \case+ Failure e -> Left e+ Success a -> Right a -- validation :: (e -> b) -> (a -> b) -> Validation e a -> b instance (Traversable t, CErr (t Bool) ~ (), CRes (t Bool) ~ t ())@@ -56,8 +58,7 @@ -- pedagogically: bifurcate = bifurcate . sequenceA instance (Traversable t, Semigroup e, CErr (t (Validation e a)) ~ e, CRes (t (Validation e a)) ~ t a) => Bifurcate (t (Validation e a)) where- bifurcate = toEither . sequenceA- -- pedagogically: bifurcate = bifurcate . sequenceA+ bifurcate = bifurcate . sequenceA -- | Collapses a structure into its inner type or an effectful error collapse :: (Error e :> es, Bifurcate f) => (CErr f -> e) -> f -> Eff es (CRes f)
test/Railroad/MonadErrorSpec.hs view
@@ -5,7 +5,6 @@ import Control.Monad.Except import Data.Functor.Identity-import Data.Validation import Railroad.MonadError import Test.Hspec @@ -27,9 +26,9 @@ describe "Predicate Operator (?>)" $ do it "passes when predicate is met" $ do- runMonadError (pure 10 ?> (> 5) $ const "too small") `shouldBe` Right 10+ runMonadError (pure (10 :: Int) ?> (> 5) $ const "too small") `shouldBe` Right 10 it "fails when predicate is not met" $ do- runMonadError (pure 4 ?> (> 5) $ const "too small") `shouldBe` Left "too small"+ runMonadError (pure (4 :: Int) ?> (> 5) $ const "too small") `shouldBe` Left "too small" describe "Recovery Operators (?~ and ??~)" $ do it "recovers to a constant value with (?~)" $ do@@ -40,21 +39,21 @@ describe "Cardinality Operators" $ do describe "(?+)" $ do it "succeeds on non-empty list" $ do- runMonadError (pure [1, 2, 3] ?+ "empty") `shouldBe` Right [1, 2, 3]+ runMonadError (pure [1, 2, 3 :: Int] ?+ "empty") `shouldBe` Right [1, 2, 3] it "fails on empty list" $ do runMonadError (pure ([] :: [Int]) ?+ "empty") `shouldBe` Left "empty" describe "(?!)" $ do let toErr = cardinalityErr "none" (const "too many") it "extracts the single element" $ do- runMonadError (pure [42] ?! toErr) `shouldBe` Right 42+ runMonadError (pure [42 :: Int] ?! toErr) `shouldBe` Right 42 it "fails on empty" $ do runMonadError (pure ([] :: [Int]) ?! toErr) `shouldBe` Left "none" it "fails on multiple elements" $ do- runMonadError (pure [1, 2] ?! toErr) `shouldBe` Left "too many"+ runMonadError (pure [1, 2 :: Int] ?! toErr) `shouldBe` Left "too many" describe "(?∅)" $ do it "succeeds on empty" $ do runMonadError (pure [] ?∅ const "not empty") `shouldBe` Right () it "fails on non-empty" $ do- runMonadError (pure [1] ?∅ const "not empty") `shouldBe` Left "not empty"+ runMonadError (pure [1 :: Int] ?∅ const "not empty") `shouldBe` Left "not empty"
test/RailroadSpec.hs view
@@ -47,11 +47,11 @@ bifurcate input1 `shouldBe` Left "first" it "List of Validation (Error Accumulation)" $ do- let input = [Success 1, Success 2] :: [Validation String Int]- bifurcate input `shouldBe` Right [1, 2]+ let allOk = [Success 1, Success 2] :: [Validation String Int]+ bifurcate allOk `shouldBe` Right [1, 2] -- Note: Validation accumulates because String is a Semigroup- let input = [Success 1, Failure "Fail A ", Failure "Fail B"] :: [Validation String Int]- bifurcate input `shouldBe` Left "Fail A Fail B"+ let someBad = [Success 1, Failure "Fail A ", Failure "Fail B"] :: [Validation String Int]+ bifurcate someBad `shouldBe` Left "Fail A Fail B" describe "Basic Operators (? and ??)" $ do it "unwraps success values with (?)" $ do@@ -66,10 +66,10 @@ describe "Predicate Operator (?>)" $ do it "passes when predicate is met" $ do- runRail (pure 10 ?> (> 5) $ const "too small") `shouldBe` Right 10+ runRail (pure (10 :: Int) ?> (> 5) $ const "too small") `shouldBe` Right 10 it "fails when predicate is not met" $ do- runRail (pure 4 ?> (> 5) $ const "too small") `shouldBe` Left "too small"+ runRail (pure (4 :: Int) ?> (> 5) $ const "too small") `shouldBe` Left "too small" describe "Recovery Operators (?~ and ??~)" $ do it "recovers to a constant value with (?~)" $ do@@ -81,21 +81,21 @@ describe "Cardinality Operators" $ do describe "(?+)" $ do it "succeeds on non-empty list" $ do- runRail (pure [1, 2, 3] ?+ "empty") `shouldBe` Right [1, 2, 3]+ runRail (pure [1, 2, 3 :: Int] ?+ "empty") `shouldBe` Right [1, 2, 3] it "fails on empty list" $ do runRail (pure ([] :: [Int]) ?+ "empty") `shouldBe` Left "empty" describe "(?!)" $ do let toErr = cardinalityErr "none" (const "too many") it "extracts the single element" $ do- runRail (pure [42] ?! toErr) `shouldBe` Right 42+ runRail (pure [42 :: Int] ?! toErr) `shouldBe` Right 42 it "fails on empty" $ do runRail (pure ([] :: [Int]) ?! toErr) `shouldBe` Left "none" it "fails on multiple elements" $ do- runRail (pure [1, 2] ?! toErr) `shouldBe` Left "too many"+ runRail (pure [1, 2 :: Int] ?! toErr) `shouldBe` Left "too many" describe "(?∅)" $ do it "succeeds on empty" $ do runRail (pure [] ?∅ const "not empty") `shouldBe` Right () it "fails on non-empty" $ do- runRail (pure [1] ?∅ const "not empty") `shouldBe` Left "not empty"+ runRail (pure [1 :: Int] ?∅ const "not empty") `shouldBe` Left "not empty"