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)
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 [(), ()]
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