open-union
Extensible, type-safe unions.
https://github.com/bfops/open-union
Latest on Hackage: | 0.4.0.0 |
This package is not currently in any snapshots. If you're interested in using it, we recommend adding it to Stackage Nightly. Doing so will make builds more reliable, and allow stackage.org to host generated Haddocks.
Extensible, type-safe unions. This package is very new and likely to change.
Basic usage example (language tags ommitted due to https://github.com/haskell/cabal/issues/774)
import Data.OpenUnion
type MyUnion = Union '[Char, Int, [()]]
showMyUnion :: MyUnion -> String
showMyUnion
= (\(c :: Char) -> "char: " ++ show c)
@> (\(i :: Int) -> "int: " ++ show i)
@> (\(l :: [()]) -> "list length: " ++ show (length l))
@> (\(s :: String) -> "string: " ++ s)
@> typesExhausted
main :: IO ()
main = do
putStrLn $ showMyUnion $ liftUnion (4 :: Int)
putStrLn $ showMyUnion $ liftUnion 'a'
putStrLn $ showMyUnion $ liftUnion [(), ()]
which prints:
int: 4
char: 'a'
list length: 2
Casting to an unrelated type does not cause errors;
In the above example,showMyUnion
contains a String
case despite MyUnion
not containing
String
- superfluous cases are ignored, for the time being.
typesExhausted
is NOT a catchall. It is a null case, and using it as a catchall
(or forgetting to provide a certain case, for instance) will result in an error like:
example.hs:12:8:
Couldn't match type ‘Int : ('[] :\ [Char])’ with ‘'[]’
Expected type: Union ('[Int] :\ String) -> String
Actual type: Union '[] -> String
In the second argument of ‘(@>)’, namely ‘typesExhausted’
In the second argument of ‘(@>)’, namely
‘(\ (s :: String) -> "string: " ++ s) @> typesExhausted’
The left-hand parts of the `:
(think type-level (:)
) are the cases that still need to be satisfied.
Trying to lift an incorrect type to a Union
will cause an error resembling:
example.hs:20:30:
No instance for (Data.OpenUnion.Internal.LiftToUnion '[] [Char])
arising from a use of ‘liftUnion’
In the second argument of ‘($)’, namely ‘liftUnion "asdf"’
In the second argument of ‘($)’, namely
‘showMyUnion $ liftUnion "asdf"’
In a stmt of a 'do' block:
putStrLn $ showMyUnion $ liftUnion "asdf"
The original use case for this library was code like this (snipped from some record/playback logic):
type TrackStates = '[Stopped, Recording, Playing]
startRecording
:: Union (TrackStates :\ Recording)
-> ([Note], Union '[Recording])
The (:\\)
type-level operator is for removal from a set, i.e.
startRecording
can be applied to a track in any state except the
Recording
state.