Data.Veither
- Package
- purescript-veither
- Repository
- JordanMartinez/purescript-veither
#Veither Source
newtype Veither errorRows a
Veither
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
'sRight
constructor: usepure
. For example,pure 4 :: forall errorRows. Veither errorRows Int
Either
'sLeft
constructor: 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
'ssafe
function)vhandle
vhandleErrors
(inspired bypurescript-checked-exceptions
'shandleErrors
function)vfromEither
genVeitherUniform
- same asgenEither
but with uniform probabilitygenVeitherFrequency
- same asgenEither
but 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 -> b
Convert 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 a
Removes 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 a
Removes 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 b
Convert 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 -> a
Extract 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 -> a
Same as vfromRight
but the default value is lazy.
#vfromLeft Source
vfromLeft :: forall errorRows a b. b -> (Variant errorRows -> b) -> Veither errorRows a -> b
Extract 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 -> b
Same 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 b
Convert 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 where
Members
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 where
Members
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 where
Members
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 where
Members
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