| Copyright | Dennis Gosnell 2017 |
|---|---|
| License | BSD3 |
| Maintainer | Dennis Gosnell ([email protected]) |
| Stability | experimental |
| Portability | unknown |
| Safe Haskell | None |
| Language | Haskell2010 |
Servant.Checked.Exceptions.Internal.Union
Description
This module defines extensible sum-types. This is similar to how vinyl defines extensible records.
This is used extensively in the definition of the Envelope type in
Servant.Checked.Exceptions.Internal.Envelope.
A large portion of the code from this module was taken from the union package.
- data Union f as where
- union :: (Union f as -> c) -> (f a -> c) -> Union f (a ': as) -> c
- catchesUnion :: (Applicative f, ToProduct tuple f (ReturnX x as)) => tuple -> Union f as -> f x
- absurdUnion :: Union f '[] -> a
- umap :: (forall a. f a -> g a) -> Union f as -> Union g as
- _This :: Prism (Union f (a ': as)) (Union f (b ': as)) (f a) (f b)
- _That :: Prism (Union f (a ': as)) (Union f (a ': bs)) (Union f as) (Union f bs)
- data Nat
- type family RIndex (r :: k) (rs :: [k]) :: Nat where ...
- class i ~ RIndex a as => UElem a as i where
- type IsMember a as = UElem a as (RIndex a as)
- type OpenUnion = Union Identity
- openUnion :: (OpenUnion as -> c) -> (a -> c) -> OpenUnion (a ': as) -> c
- fromOpenUnion :: (OpenUnion as -> a) -> OpenUnion (a ': as) -> a
- fromOpenUnionOr :: OpenUnion (a ': as) -> (OpenUnion as -> a) -> a
- openUnionPrism :: forall a as. IsMember a as => Prism' (OpenUnion as) a
- openUnionLift :: forall a as. IsMember a as => a -> OpenUnion as
- openUnionMatch :: forall a as. IsMember a as => OpenUnion as -> Maybe a
- catchesOpenUnion :: ToOpenProduct tuple (ReturnX x as) => tuple -> OpenUnion as -> x
Union
data Union f as where Source #
A Union is parameterized by a universe u, an interpretation f
and a list of labels as. The labels of the union are given by
inhabitants of the kind u; the type of values at any label a ::
u is given by its interpretation f a :: *.
Instances
| (Eq (f a1), Eq (Union a f as)) => Eq (Union a f ((:) a a1 as)) Source # | |
| Eq (Union u f ([] u)) Source # | |
| (Ord (f a1), Ord (Union a f as)) => Ord (Union a f ((:) a a1 as)) Source # | |
| Ord (Union u f ([] u)) Source # | |
| (Read (f a1), Read (Union a f as)) => Read (Union a f ((:) a a1 as)) Source # | This is only a valid instance when the For instance, imagine we are working with a
However, imagine are we working with a
If the order of the types is flipped around, we are are able to read
|
| Read (Union u f ([] u)) Source # | This will always fail, since |
| (Show (f a1), Show (Union a f as)) => Show (Union a f ((:) a a1 as)) Source # | |
| Show (Union u f ([] u)) Source # | |
| (ToJSON (f a1), ToJSON (Union a f as)) => ToJSON (Union a f ((:) a a1 as)) Source # | |
| ToJSON (Union u f ([] u)) Source # | |
| (FromJSON (f a1), FromJSON (Union a f as)) => FromJSON (Union a f ((:) a a1 as)) Source # | This is only a valid instance when the This is similar to the |
| FromJSON (Union u f ([] u)) Source # | This will always fail, since |
| (NFData (f a1), NFData (Union a f as)) => NFData (Union a f ((:) a a1 as)) Source # | |
| NFData (Union u f ([] u)) Source # | |
union :: (Union f as -> c) -> (f a -> c) -> Union f (a ': as) -> c Source #
Case analysis for Union.
Here is an example of matching on a This:
>>>let u = This (Identity "hello") :: Union Identity '[String, Int]>>>let runIdent = runIdentity :: Identity String -> String>>>union (const "not a String") runIdent u"hello"
Here is an example of matching on a That:
>>>let v = That (This (Identity 3.3)) :: Union Identity '[String, Double, Int]>>>union (const "not a String") runIdent v"not a String"
catchesUnion :: (Applicative f, ToProduct tuple f (ReturnX x as)) => tuple -> Union f as -> f x Source #
An alternate case anaylsis for a Union. This method uses a tuple
containing handlers for each potential value of the Union. This is
somewhat similar to the catches function.
Here is an example of handling a Union with two possible values. Notice
that a normal tuple is used:
>>>let u = This $ Identity 3 :: Union Identity '[Int, String]>>>let intHandler = (Identity $ \int -> show int) :: Identity (Int -> String)>>>let strHandler = (Identity $ \str -> str) :: Identity (String -> String)>>>catchesUnion (intHandler, strHandler) u :: Identity StringIdentity "3"
Given a Union like , the type of
Union Identity '[Int, String]catchesUnion becomes the following:
catchesUnion:: (Identity(Int->String),Identity(String->String)) ->UnionIdentity'[Int,String] ->IdentityString
Checkout catchesOpenUnion for more examples.
absurdUnion :: Union f '[] -> a Source #
Since a union with an empty list of labels is uninhabited, we can recover any type from it.
Optics
_This :: Prism (Union f (a ': as)) (Union f (b ': as)) (f a) (f b) Source #
Lens-compatible Prism for This.
Use _This to construct a Union:
>>>review _This (Just "hello") :: Union Maybe '[String]Just "hello"
Use _This to try to destruct a Union into a f a:
>>>let u = This (Identity "hello") :: Union Identity '[String, Int]>>>preview _This u :: Maybe (Identity String)Just (Identity "hello")
Use _This to try to destruct a Union into a f a (unsuccessfully):
>>>let v = That (This (Identity 3.3)) :: Union Identity '[String, Double, Int]>>>preview _This v :: Maybe (Identity String)Nothing
_That :: Prism (Union f (a ': as)) (Union f (a ': bs)) (Union f as) (Union f bs) Source #
Lens-compatible Prism for That.
Use _That to construct a Union:
>>>let u = This (Just "hello") :: Union Maybe '[String]>>>review _That u :: Union Maybe '[Double, String]Just "hello"
Use _That to try to peel off a That from a Union:
>>>let v = That (This (Identity "hello")) :: Union Identity '[Int, String]>>>preview _That v :: Maybe (Union Identity '[String])Just (Identity "hello")
Use _That to try to peel off a That from a Union (unsuccessfully):
>>>let w = This (Identity 3.5) :: Union Identity '[Double, String]>>>preview _That w :: Maybe (Union Identity '[String])Nothing
Typeclasses
A mere approximation of the natural numbers. And their image as lifted by
-XDataKinds corresponds to the actual natural numbers.
type family RIndex (r :: k) (rs :: [k]) :: Nat where ... Source #
A partial relation that gives the index of a value in a list.
Find the first item:
>>>import Data.Type.Equality ((:~:)(Refl))>>>Refl :: RIndex String '[String, Int] :~: 'ZRefl
Find the third item:
>>>Refl :: RIndex Char '[String, Int, Char] :~: 'S ('S 'Z)Refl
class i ~ RIndex a as => UElem a as i where Source #
provides a way to potentially get an UElem a as if a out of a
(Union f asunionMatch). It also provides a way to create a
from an Union f asf a (unionLift).
This is safe because of the RIndex contraint. This RIndex constraint
tells us that there actually is an a in as at index i.
As an end-user, you should never need to implement an additional instance of this typeclass.
Minimal complete definition
Methods
unionPrism :: Prism' (Union f as) (f a) Source #
This is implemented as .prism' unionLift unionMatch
unionLift :: f a -> Union f as Source #
This is implemented as .review unionPrism
unionMatch :: Union f as -> Maybe (f a) Source #
This is implemented as .preview unionPrism
type IsMember a as = UElem a as (RIndex a as) Source #
This is a helpful Constraint synonym to assert that a is a member of
as.
OpenUnion
openUnion :: (OpenUnion as -> c) -> (a -> c) -> OpenUnion (a ': as) -> c Source #
Case analysis for OpenUnion.
Here is an example of successfully matching:
>>>let string = "hello" :: String>>>let o = openUnionLift string :: OpenUnion '[String, Int]>>>openUnion (const "not a String") id o"hello"
Here is an example of unsuccessfully matching:
>>>let double = 3.3 :: Double>>>let p = openUnionLift double :: OpenUnion '[String, Double, Int]>>>openUnion (const "not a String") id p"not a String"
fromOpenUnion :: (OpenUnion as -> a) -> OpenUnion (a ': as) -> a Source #
This is similar to fromMaybe for an OpenUnion.
Here is an example of successfully matching:
>>>let string = "hello" :: String>>>let o = openUnionLift string :: OpenUnion '[String, Int]>>>fromOpenUnion (const "not a String") o"hello"
Here is an example of unsuccessfully matching:
>>>let double = 3.3 :: Double>>>let p = openUnionLift double :: OpenUnion '[String, Double, Int]>>>fromOpenUnion (const "not a String") p"not a String"
fromOpenUnionOr :: OpenUnion (a ': as) -> (OpenUnion as -> a) -> a Source #
Flipped version of fromOpenUnion.
openUnionPrism :: forall a as. IsMember a as => Prism' (OpenUnion as) a Source #
Just like unionPrism but for OpenUnion.
openUnionLift :: forall a as. IsMember a as => a -> OpenUnion as Source #
openUnionMatch :: forall a as. IsMember a as => OpenUnion as -> Maybe a Source #
Just like unionMatch but for OpenUnion.
Successful matching:
>>>let string = "hello" :: String>>>let o = openUnionLift string :: OpenUnion '[Double, String, Int]>>>openUnionMatch o :: Maybe StringJust "hello"
Failure matching:
>>>let double = 3.3 :: Double>>>let p = openUnionLift double :: OpenUnion '[Double, String]>>>openUnionMatch p :: Maybe StringNothing
catchesOpenUnion :: ToOpenProduct tuple (ReturnX x as) => tuple -> OpenUnion as -> x Source #
An alternate case anaylsis for an OpenUnion. This method uses a tuple
containing handlers for each potential value of the OpenUnion. This is
somewhat similar to the catches function.
Here is an example of handling an OpenUnion with two possible values.
Notice that a normal tuple is used:
>>>let u = openUnionLift (3 :: Int) :: OpenUnion '[Int, String]>>>let intHandler = (\int -> show int) :: Int -> String>>>let strHandler = (\str -> str) :: String -> String>>>catchesOpenUnion (intHandler, strHandler) u :: String"3"
Given an OpenUnion like , the type of
OpenUnion '[Int, String]catchesOpenUnion becomes the following:
catchesOpenUnion:: (Int-> x,String-> x) ->OpenUnion'[Int,String] -> x
Here is an example of handling an OpenUnion with three possible values:
>>>let u = openUnionLift ("hello" :: String) :: OpenUnion '[Int, String, Double]>>>let intHandler = (\int -> show int) :: Int -> String>>>let strHandler = (\str -> str) :: String -> String>>>let dblHandler = (\dbl -> "got a double") :: Double -> String>>>catchesOpenUnion (intHandler, strHandler, dblHandler) u :: String"hello"
Here is an example of handling an OpenUnion with only one possible value.
Notice how a tuple is not used, just a single value:
>>>let u = openUnionLift (2.2 :: Double) :: OpenUnion '[Double]>>>let dblHandler = (\dbl -> "got a double") :: Double -> String>>>catchesOpenUnion dblHandler u :: String"got a double"
When working with large OpenUnions, it can be easier to use
catchesOpenUnion than openUnion.
Setup code for doctests
>>>:set -XDataKinds>>>:set -XTypeOperators>>>import Data.Text (Text)>>>import Text.Read (readMaybe)