{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
{-# LANGUAGE OverlappingInstances #-}
{-# LANGUAGE UndecidableInstances #-}
-- | Original work at: http://okmij.org/ftp/Haskell/extensible/OpenUnion1.hs.
-- Open unions (type-indexed co-products) for extensible effects.
-- This implementation relies on _closed_ overlapping instances
-- (or closed type function overlapping soon to be added to GHC).
module Data.OpenUnion1( Union
, inj
, prj
, prjForce
, decomp
, Member
, (:>)
, unsafeReUnion
) where
import Control.Applicative ((<$>))
import Data.Typeable
infixl 4 <?>
-- | infix form of `fromMaybe`.
(<?>) :: Maybe a -> a -> a
Just a <?> _ = a
_ <?> a = a
-- for the sake of gcast1
newtype Id a = Id { runId :: a }
-- | Where `r` is `t1 :> t2 ... :> tn`, `Union r v` can be constructed with a
-- value of type `ti v`.
-- Ideally, we should be be able to add the constraint `Member t r`.
data Union r v = forall t. (Functor t, Typeable1 t) => Union (t v)
instance Functor (Union r) where
{-# INLINE fmap #-}
fmap f (Union v) = Union (fmap f v)
-- | A sum data type, for `composing' effects
-- In GHC 7.4, we should make it a list
-- (:>) :: (* -> *) -> (* -> List) -> List
infixr 1 :>
data ((a :: * -> *) :> b)
class Member (t :: * -> *) r
instance Member t (t :> r)
instance Member t r => Member t (t' :> r)
{-# INLINE inj #-}
-- | Construct a Union.
inj :: (Functor t, Typeable1 t, Member t r) => t v -> Union r v
inj = Union
{-# INLINE prj #-}
-- | Try extracting the contents of a Union as a specific type.
prj :: (Typeable1 t, Member t r) => Union r v -> Maybe (t v)
prj (Union v) = runId <$> gcast1 (Id v)
{-# INLINE prjForce #-}
-- Like `prj`, but returns an error if the cast fails.
prjForce :: (Typeable1 t, Member t r) => Union r v -> (t v -> a) -> a
prjForce u f = f <$> prj u <?> error "prjForce Nothing"
{-# INLINE decomp #-}
decomp :: (Typeable1 t, Member t (t :> r)) => Union (t :> r) v -> Either (Union r v) (t v)
decomp u = Right <$> prj u <?> Left (unsafeReUnion u)
{-# INLINE unsafeReUnion #-}
-- | Juggle types for a Union. Use cautiously.
unsafeReUnion :: Union r w -> Union t w
unsafeReUnion (Union v) = Union v