{-# LANGUAGE CPP #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE GADTs #-} #if __GLASGOW_HASKELL__ >= 708 {-# LANGUAGE AutoDeriveTypeable #-} #endif {-# LANGUAGE Safe #-} module Control.Monad.Free.Reflection( Free , freePure , freeImpure , freeMap , FreeView (..) , fromView , toView ) where import Control.Arrow (Kleisli (..)) import qualified Data.OpenUnion.Imports as P import Control.Monad import Data.TASequence import Data.TASequence.FastCatQueue (FastTCQueue) -- | Specific type-aligned sequence used to store chain of monadic binds. type TCQ = FastTCQueue -- | Type used to denote monadic chain of binds. As expected this builds on the -- Kleisli representation. type FreeExp f a b = TCQ (Kleisli (Free f)) a b -- | The abstract Free datatype. Original work available at -- . data Free f a = forall x. Free (FreeView f x) (FreeExp f x a) -- | Inject a pure value into Free freePure :: a -> Free f a freePure = fromView . Pure -- | Inject an impure value into Free freeImpure :: f (Free f a) -> Free f a freeImpure = fromView . Impure -- | Case analysis for the 'Free' construction. Similar in spirit to 'either' -- and 'maybe'. freeMap :: Functor f => (a -> t) -- ^ function to be applied if value is Pure -> (f (Free f a) -> t) -- ^ function to be applied on Impure value -> Free f a -- ^ Free value to be mapped over -> t -- ^ result freeMap f g mx = case toView mx of Pure x -> f x Impure u -> g u instance Functor f => Functor (Free f) where fmap = liftM instance Functor f => P.Applicative (Free f) where pure = return (<*>) = ap instance Functor f => Monad (Free f) where return = freePure mx >>= f = mx ^>>= tsingleton (Kleisli f) -- | The traditional 'view' of Free constructions data FreeView f a = Pure a -- ^ case embedding pure values | Impure (f (Free f a)) -- ^ case embedding impure values -- nested in @f@. Traditionally this -- is the @Control.Monad.Free.Free@ -- constructor, but that's confusing. -- | A way to get a 'Free' construction from the view by constructing an -- explicit expression with one element. fromView :: FreeView f a -> Free f a fromView x = Free x tempty -- | A way to evaluate the 'Free' construction to its view (i.e., head normal -- form). This includes the logic to perform one level of monadic bind as needed -- from the 'FreeExp' representation. toView :: Functor f => Free f a -> FreeView f a toView (Free h t) = case h of Pure x -> case tviewl t of TAEmptyL -> Pure x hc :< tc -> toView (runKleisli hc x ^>>= tc) Impure f -> Impure (fmap (^>>= t) f) -- | The essence of monadic '>>=', i.e., append/concatenation (of sorts) (^>>=) :: Free f a -> FreeExp f a b -> Free f b (Free x ys) ^>>= r = Free x (ys >< r)