{-# LANGUAGE CPP #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.OpenUnion.Internal
( Union (..)
, (@>)
, (@!>)
, liftUnion
, reUnion
, flattenUnion
, restrict
, typesExhausted
) where
import Control.Exception
import Data.Dynamic
import TypeFun.Data.List (SubList, Elem, Delete, (:++:))
#if MIN_VERSION_base(4,10,0)
import Data.Proxy
import Data.Typeable
#endif
newtype Union (s :: [*]) = Union Dynamic
instance Show (Union '[]) where
show = typesExhausted
instance (Show a, Show (Union (Delete a as)), Typeable a)
=> Show (Union (a ': as)) where
show u = case restrict u of
Left sub -> show sub
Right (a :: a) ->
let p = Proxy :: Proxy a
rep = typeRep p
in "Union (" ++ show a ++ " :: " ++ show rep ++ ")"
instance Eq (Union '[]) where
a == _ = typesExhausted a
instance (Typeable a, Eq (Union (Delete a as)), Eq a)
=> Eq (Union (a ': as)) where
u1 == u2 =
let r1 = restrict u1
r2 = restrict u2
in case (r1, r2) of
(Right (a :: a), Right b) -> a == b
(Left a , Left b) -> a == b
_ -> False
instance Ord (Union '[]) where
compare a _ = typesExhausted a
instance (Ord a, Typeable a, Ord (Union (Delete a as)))
=> Ord (Union (a ': as)) where
compare u1 u2 =
let r1 = restrict u1
r2 = restrict u2
in case (r1, r2) of
(Right (a :: a), Right b) -> compare a b
(Left a , Left b) -> compare a b
(Right _ , Left _) -> GT
(Left _ , Right _) -> LT
instance (Exception e) => Exception (Union (e ': '[])) where
toException u = case restrict u of
Left (sub :: Union '[]) -> typesExhausted sub
Right (e :: e) -> toException e
fromException some = case fromException some of
Just (e :: e) -> Just (liftUnion e)
Nothing -> Nothing
instance ( Exception e, Typeable e, Typeable es, Typeable e1
, Exception (Union (Delete e (e1 ': es)))
, SubList (Delete e (e1 ': es)) (e ': e1 ': es) )
=> Exception (Union (e ': e1 ': es)) where
toException u = case restrict u of
Left (sub :: Union (Delete e (e1 ': es))) -> toException sub
Right (e :: e) -> toException e
fromException some = case fromException some of
Just (e :: e) -> Just (liftUnion e)
Nothing ->
let sub :: Maybe (Union (Delete e (e1 ': es)))
sub = fromException some
in fmap reUnion sub
type family FlatElems a :: [*] where
FlatElems '[] = '[]
FlatElems ((Union s) : ss) = s :++: FlatElems ss
FlatElems (x : s) = x : FlatElems s
(@>) :: Typeable a
=> (a -> b)
-> (Union (Delete a s) -> b)
-> Union s
-> b
r @> l = either l r . restrict
infixr 2 @>
{-# INLINE (@>) #-}
(@!>) :: (Typeable a, Elem a s)
=> (a -> b)
-> (Union (Delete a s) -> b)
-> Union s
-> b
r @!> l = either l r . restrict
infixr 2 @!>
{-# INLINE (@!>) #-}
liftUnion :: (Typeable a, Elem a s) => a -> Union s
liftUnion = Union . toDyn
{-# INLINE liftUnion #-}
restrict :: Typeable a => Union s -> Either (Union (Delete a s)) a
restrict (Union d) = maybe (Left $ Union d) Right $ fromDynamic d
{-# INLINE restrict #-}
reUnion :: (SubList s s') => Union s -> Union s'
reUnion (Union d) = Union d
{-# INLINE reUnion #-}
flattenUnion :: Union s -> Union (FlatElems s)
flattenUnion (Union d) = Union d
{-# INLINE flattenUnion #-}
typesExhausted :: Union '[] -> a
typesExhausted = error "Union types exhausted - empty Union"
{-# INLINE typesExhausted #-}