Data.Veither
- Package
- purescript-veither
- Repository
- JordanMartinez/purescript-veither
#Veither Source
newtype Veither errorRows aVeither is the same as Either except that the l type can be zero to many different types.
Veither has all the instances that Either has, except for Eq1 and Ord1, which simply
haven't been implemented yet. If you would use a function from Data.Either (e.g. hush) and
you want to use the equivalent for Veither, add a v in front of it (e.g. vhush).
Conceptually, Veither has the following definition:
data Veither l1 l2 ... ln a
= Right a
| L1 l1
| L2 l2
| ...
| LN ln
Veither is monadic via the a type parameter. For example, the Int type below
represents the 'happy path' and any other errors will short-circuit the computation:
foo :: Variant (e1 :: Error1, e2 :: Error2) Int
foo = do
i1 <- returnIntOrFailWithError1
i2 <- returnIntOrFailWithError2
pure $ i1 + i2
Creating a value of Veither can be done in one of two ways, depending on whether
you want the resulting Veither to function like Either's Right constructor or like
Either's Left constructor:
Either'sRightconstructor: usepure. For example,pure 4 :: forall errorRows. Veither errorRows IntEither'sLeftconstructor: useData.Variant.inj. For example,Veither (inj (Proxy :: Proxy "foo") String)) :: forall a. Veither (foo :: String) a
One can also change an Either a b into a Veither (x :: a) b using vfromEither.
To consume a Veither value, use veither, vfromRight, vfromLeft, vnote, or vhush. For example,
one might do the following using veither:
import Type.Proxy (Proxy(..))
import Data.Variant (case_, on, inj)
-- Given a variant value...
val :: Veither (a :: Int, b :: String, c :: Boolean) Number
val = pure 5
-- you consume it using the following pattern. You'll need to handle every possible error type
consume :: Veither (a :: Int, b :: String, c :: Boolean) Number -> String
consume v = veither handleError handleSuccess v
where
handleError :: Variant (a :: Int, b :: String, c :: Boolean)
handleError =
case_
# on (Proxy :: Proxy "a") show
# on (Proxy :: Proxy "b") show
# on (Proxy :: Proxy "c") show
handleSuccess :: Number -> String
handleSuccess = show
Below are functions that exist in Veither but do not exist in Either:
vsafe(inspired bypurescript-checked-exceptions'ssafefunction)vhandlevhandleErrors(inspired bypurescript-checked-exceptions'shandleErrorsfunction)vfromEithergenVeitherUniform- same asgenEitherbut with uniform probabilitygenVeitherFrequency- same asgenEitherbut with user-specified probability
Constructors
Instances
Newtype (Veither errorRows a) _Foldable (Veither errorRows)Traversable (Veither errorRows)Invariant (Veither errorRows)Functor (Veither errorRows)FunctorWithIndex Unit (Veither errorRows)Apply (Veither errorRows)Applicative (Veither errorRows)Bind (Veither errorRows)Monad (Veither errorRows)Alt (Veither errorRows)Extend (Veither errorRows)(Show (Variant (_ :: a | errorRows))) => Show (Veither errorRows a)(Eq (Variant (_ :: a | errorRows))) => Eq (Veither errorRows a)(Ord (Variant (_ :: a | errorRows))) => Ord (Veither errorRows a)(Bounded (Variant (_ :: a | errorRows))) => Bounded (Veither errorRows a)(Enum (Variant (_ :: a | errorRows))) => Enum (Veither errorRows a)(BoundedEnum (Variant (_ :: a | errorRows))) => BoundedEnum (Veither errorRows a)(Semigroup b) => Semigroup (Veither errorRows b)(RowToList (_ :: a | errorRows) rowList, VariantArbitrarys (_ :: a | errorRows) rowList) => Arbitrary (Veither errorRows a)(RowToList (_ :: a | errorRows) rl, VariantCoarbitrarys rl) => Coarbitrary (Veither errorRows a)
#veither Source
veither :: forall errorRows a b. (Variant errorRows -> b) -> (a -> b) -> Veither errorRows a -> bConvert a Veither into a value by defining how to handle each possible value.
Below is an example of the typical usage.
consume :: Veither (a :: Int, b :: String, c :: Boolean) Number -> String
consume v = veither handleError handleSuccess v
where
handleError :: Variant (a :: Int, b :: String, c :: Boolean)
handleError =
case_
# on (Proxy :: Proxy "a") show
# on (Proxy :: Proxy "b") show
# on (Proxy :: Proxy "c") show
handleSuccess :: Number -> String
handleSuccess = show
#vhandle Source
vhandle :: forall sym b otherErrorRows errorRows a. IsSymbol sym => Cons sym b otherErrorRows errorRows => Proxy sym -> (b -> a) -> Veither errorRows a -> Veither otherErrorRows aRemoves one of the possible error types in the Veither by converting its value
to a value of type a, the 'happy path' type. This can be useful for gradually
picking off some of the errors the Veither value could have by handling only
some of them at a given point in your code.
If the number of errors in your Veither are small and can all be handled via vhandle,
one can use vsafe to extract the value of the 'happy path' a type.
foo :: Veither (b :: Int) String
foo = pure "2"
_b :: Proxy "b"
_b = Proxy
bar :: Veither (b :: Int) String
bar = Veither (inj_ _b 3)
vhandle _b show bar == ((pure "3") :: Veither () String)
vhandle _b show foo == ((pure "2") :: Veither () String)
safe (vhandle _b show bar) == "3"
safe (vhandle _b show foo) == "2"
#vhandleErrors Source
vhandleErrors :: forall handlers rlHandlers handledRows remainingErrorRows allErrorRows a. RowToList handlers rlHandlers => VariantMatchCases rlHandlers handledRows a => Union handledRows (_ :: a | remainingErrorRows) (_ :: a | allErrorRows) => Record handlers -> Veither allErrorRows a -> Veither remainingErrorRows aRemoves one, some, or all of the possible error types in the Veither
by converting its value to a value of type a, the 'happy path' type.
Note: you will get a compiler error unless you add annotations
to the record argument. You can do this by defining defining the record
using a let statement or by annotating it inline
(e.g. { a: identity} :: { a :: Int -> Int }`).
If all errors are handled via vhandleErrors,
one can use vsafe to extract the value of the 'happy path' a type.
Doing something like vhandleErrors {"_": \(i :: Int) -> i} v will
fail to compile. If you want to handle all possible values in the
Veither, use veither or Data.Variant.onMatch directly
(e.g. onMatch record <<< un Veither) instead of this function.
Example usage:
_a :: Proxy "a"
_a = Proxy
_b :: Proxy "b"
_b = Proxy
va :: Veither (a :: Int, b :: Boolean, c :: List String) String
va = Veither $ inj _a 4
vb :: Veither (a :: Int, b :: Boolean, c :: List String) String
vb = Veither $ inj _b false
handlers :: Record (a :: Int -> String, b :: Boolean -> String)
handlers = { a: show, b: show }
vhandleErrors handlers va == ((pure "4") :: Veither (c :: List String) String)
vhandleErrors handlers vb == ((pure "false") :: Veither (c :: List String) String)
#vfromEither Source
vfromEither :: forall sym otherRows errorRows a b. IsSymbol sym => Cons sym a otherRows (_ :: b | errorRows) => Proxy sym -> Either a b -> Veither errorRows bConvert an Either into a Veither.
p :: Proxy "foo"
p = Proxy
vfromEither p (Left Int) :: forall a. Variant (foo :: Int) a
vfromEither p (Right Int) :: forall a. Variant (foo :: a ) Int
#vfromRight Source
vfromRight :: forall errorRows a. a -> Veither errorRows a -> aExtract the value from a Veither, using a default value in case the underlying
Variant is storing one of the error rows' values.
vError :: Veither (foo :: Int) String
vError = Veither (inj (Proxy :: Proxy "foo") 4)
vSuccess :: Veither (foo :: Int) String
vSuccess = pure "yay"
vfromRight "" vError == ""
vfromRight "" vSuccess == "yay"
#vfromRight' Source
vfromRight' :: forall errorRows a. (Unit -> a) -> Veither errorRows a -> aSame as vfromRight but the default value is lazy.
#vfromLeft Source
vfromLeft :: forall errorRows a b. b -> (Variant errorRows -> b) -> Veither errorRows a -> bExtract the error value from a Veither, using a default value in case the underlying
Variant is storing the ("_" :: a) rows' values.
vError :: Veither (foo :: Int) String
vError = Veither (inj (Proxy :: Proxy "foo") 4)
vSuccess :: Veither (foo :: Int) String
vSuccess = pure "yay"
vfromLeft 8 (case_ # on (Proxy :: Proxy "foo") identity) vError == 4
vfromRight 8 (case_ # on (Proxy :: Proxy "foo") identity) vSuccess == 8
#vfromLeft' Source
vfromLeft' :: forall errorRows a b. (Unit -> b) -> (Variant errorRows -> b) -> Veither errorRows a -> bSame as vfromLeft but the default value is lazy.
#vnote Source
vnote :: forall otherErrorRows errorRows s a b. Cons s a otherErrorRows (_ :: b | errorRows) => IsSymbol s => Proxy s -> a -> Maybe b -> Veither errorRows bConvert a Maybe value into a Veither value using a default value when the Maybe value is Nothing.
mJust :: Maybe String
mJust = Just "x"
mNothing :: Maybe String
mNothing = Nothing
_foo :: Proxy "foo"
_foo = Proxy
vnote _foo 2 mJust == (pure "y") :: Veither (foo :: Int) String
vnote _foo 2 mNothing == (Veither (inj _foo 2)) :: Veither (foo :: Int) String
#genVeitherUniform Source
genVeitherUniform :: forall a errorRows otherGenRows rowList. RowToList (_ :: Gen a | otherGenRows) rowList => GenVariantUniform (_ :: Gen a | otherGenRows) rowList (_ :: a | errorRows) => { _ :: Gen a | otherGenRows } -> Gen (Veither errorRows a)Generate Veither with uniform probability given a record whose
generators' labels correspond to the Veither's labels
-- Note: type annotations are needed! Otherwise, you'll get compiler errors.
quickCheckGen do
v <- genVeitherUniform
-- first approach: annotate inline
{ "_": genHappyPath :: Gen Int
, x: genXValues :: Gen (Maybe String)
, y: pure "foo" :: Gen String
}
-- rest of test...
quickCheckGen do
let
-- second approach: use a let with annotations before usage
r :: { "_" :: Gen Int, x :: Gen (Maybe String), y :: Gen String }
r = { "_": genHappyPath, x: genXValues, y: pure "foo" }
v <- genVeitherUniform r
-- rest of test...
#genVeitherFrequncy Source
genVeitherFrequncy :: forall a errorRows otherGenRows rowList. RowToList (_ :: Tuple Number (Gen a) | otherGenRows) rowList => GenVariantFrequency (_ :: Tuple Number (Gen a) | otherGenRows) Number rowList (_ :: a | errorRows) => { _ :: Tuple Number (Gen a) | otherGenRows } -> Gen (Veither errorRows a)Generate Veither with user-specified probability given a record whose
generators' labels correspond to the Veither's labels
-- Note: type annotations are needed! Otherwise, you'll get compiler errors.
quickCheckGen do
v <- genVeitherFrequency
-- first approach: annotate inline
{ "_": genHappyPath :: Gen Int
, x: genXValues :: Gen (Maybe String)
, y: pure "foo" :: Gen String
}
-- rest of test...
quickCheckGen do
let
-- second approach: use a let with annotations before usage
r :: { "_" :: Gen Int, x :: Gen (Maybe String), y :: Gen String }
r = { "_": genHappyPath, x: genXValues, y: pure "foo" }
v <- genVeitherFrequency r
-- rest of test...
#VariantArbitrarys Source
class VariantArbitrarys finalRow currentRL whereMembers
Instances
VariantArbitrarys ignore Nil(IsSymbol sym, VariantArbitrarys final rlTail, Cons sym a rowTail final, Arbitrary a) => VariantArbitrarys final (Cons sym a rlTail)
#UnknownVariantValue Source
data UnknownVariantValue :: Type#VariantCoarbitrarys Source
class VariantCoarbitrarys currentRL whereMembers
variantCoarbitrarys :: forall r. Proxy currentRL -> { type :: String, value :: UnknownVariantValue } -> (Gen r -> Gen r)
Instances
VariantCoarbitrarys Nil(IsSymbol sym, Coarbitrary a, VariantCoarbitrarys tail) => VariantCoarbitrarys (Cons sym a tail)
#GenVariantUniform Source
class GenVariantUniform recordRows rl variantRows | recordRows -> variantRows whereMembers
Instances
GenVariantUniform ignore1 Nil ignore2(Cons sym (Gen a) other recordRows, IsSymbol sym, Cons sym a otherVariantRows variantRows, GenVariantUniform recordRows tail variantRows) => GenVariantUniform recordRows (Cons sym (Gen a) tail) variantRows
#GenVariantFrequency Source
class GenVariantFrequency recordRows b rl variantRows | recordRows -> b variantRows whereMembers
Instances
GenVariantFrequency ignore1 ignore2 Nil ignore3(Cons sym (Tuple b (Gen a)) other recordRows, IsSymbol sym, Cons sym a otherVariantRows variantRows, GenVariantFrequency recordRows b tail variantRows) => GenVariantFrequency recordRows b (Cons sym (Tuple b (Gen a)) tail) variantRows
- Modules
- Data.
Veither