{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
#include "free-common.h"
module Control.Monad.Free.Church
( F(..)
, improve
, fromF
, iter
, iterM
, toF
, retract
, hoistF
, foldF
, MonadFree(..)
, liftF
, cutoff
) where
import Control.Applicative
import Control.Monad as Monad
import Control.Monad.Fix
import Control.Monad.Free hiding (retract, iter, iterM, cutoff)
import Control.Monad.Reader.Class
import Control.Monad.Writer.Class
import Control.Monad.Cont.Class
import Control.Monad.Trans.Class
import Control.Monad.State.Class
import Data.Foldable
import Data.Traversable
import Data.Functor.Bind
import Data.Semigroup.Foldable
import Data.Semigroup.Traversable
import Prelude hiding (foldr)
newtype F f a = F { runF :: forall r. (a -> r) -> (f r -> r) -> r }
iter :: (f a -> a) -> F f a -> a
iter phi xs = runF xs id phi
iterM :: Monad m => (f (m a) -> m a) -> F f a -> m a
iterM phi xs = runF xs return phi
instance Functor (F f) where
fmap f (F g) = F (\kp -> g (kp . f))
instance Apply (F f) where
(<.>) = (<*>)
instance Applicative (F f) where
pure a = F (\kp _ -> kp a)
F f <*> F g = F (\kp kf -> f (\a -> g (kp . a) kf) kf)
instance Alternative f => Alternative (F f) where
empty = F (\_ kf -> kf empty)
F f <|> F g = F (\kp kf -> kf (pure (f kp kf) <|> pure (g kp kf)))
instance Bind (F f) where
(>>-) = (>>=)
instance Monad (F f) where
return = pure
F m >>= f = F (\kp kf -> m (\a -> runF (f a) kp kf) kf)
instance MonadFix (F f) where
mfix f = a where
a = f (impure a)
impure (F x) = x id (error "MonadFix (F f): wrap")
instance Foldable f => Foldable (F f) where
foldMap f xs = runF xs f fold
{-# INLINE foldMap #-}
foldr f r xs = runF xs f (foldr (.) id) r
{-# INLINE foldr #-}
#if MIN_VERSION_base(4,6,0)
foldl' f z xs = runF xs (\a !r -> f r a) (flip $ foldl' $ \r g -> g r) z
{-# INLINE foldl' #-}
#endif
instance Traversable f => Traversable (F f) where
traverse f m = runF m (fmap return . f) (fmap wrap . sequenceA)
{-# INLINE traverse #-}
instance Foldable1 f => Foldable1 (F f) where
foldMap1 f m = runF m f fold1
instance Traversable1 f => Traversable1 (F f) where
traverse1 f m = runF m (fmap return . f) (fmap wrap . sequence1)
instance MonadPlus f => MonadPlus (F f) where
mzero = F (\_ kf -> kf mzero)
F f `mplus` F g = F (\kp kf -> kf (return (f kp kf) `mplus` return (g kp kf)))
instance MonadTrans F where
lift f = F (\kp kf -> kf (liftM kp f))
instance Functor f => MonadFree f (F f) where
wrap f = F (\kp kf -> kf (fmap (\ (F m) -> m kp kf) f))
instance MonadState s m => MonadState s (F m) where
get = lift get
put = lift . put
instance MonadReader e m => MonadReader e (F m) where
ask = lift ask
local f = lift . local f . retract
instance MonadWriter w m => MonadWriter w (F m) where
tell = lift . tell
pass = lift . pass . retract
listen = lift . listen . retract
instance MonadCont m => MonadCont (F m) where
callCC f = lift $ callCC (retract . f . fmap lift)
retract :: Monad m => F m a -> m a
retract (F m) = m return Monad.join
{-# INLINE retract #-}
hoistF :: (forall x. f x -> g x) -> F f a -> F g a
hoistF t (F m) = F (\p f -> m p (f . t))
foldF :: Monad m => (forall x. f x -> m x) -> F f a -> m a
foldF f (F m) = m return (Monad.join . f)
fromF :: MonadFree f m => F f a -> m a
fromF (F m) = m return wrap
{-# INLINE fromF #-}
toF :: Functor f => Free f a -> F f a
toF xs = F (\kp kf -> go kp kf xs) where
go kp _ (Pure a) = kp a
go kp kf (Free fma) = kf (fmap (go kp kf) fma)
improve :: Functor f => (forall m. MonadFree f m => m a) -> Free f a
improve m = fromF m
{-# INLINE improve #-}
{-# INLINE cutoff #-}
cutoff :: (Functor f) => Integer -> F f a -> F f (Maybe a)
cutoff n m
| n <= 0 = return Nothing
| n <= toInteger (maxBound :: Int) = cutoffI (fromInteger n :: Int) m
| otherwise = cutoffI n m
{-# SPECIALIZE cutoffI :: (Functor f) => Int -> F f a -> F f (Maybe a) #-}
{-# SPECIALIZE cutoffI :: (Functor f) => Integer -> F f a -> F f (Maybe a) #-}
cutoffI :: (Functor f, Integral n) => n -> F f a -> F f (Maybe a)
cutoffI n m = F m' where
m' kp kf = runF m kpn kfn n where
kpn a i
| i <= 0 = kp Nothing
| otherwise = kp (Just a)
kfn fr i
| i <= 0 = kp Nothing
| otherwise = let
i' = i - 1
in i' `seq` kf (fmap ($ i') fr)