|
| 1 | +-- | This module defines a lazy implementation of the _cofree monad transformer_. |
| 2 | + |
| 3 | +-- | Given a `CofreeT` f m a: |
| 4 | +-- | - 'f' is a `Functor`, generally representing an AST, |
| 5 | +-- | - 'm' is a 'Monad', generally representing an effect, |
| 6 | +-- | - and 'a' is the type of the annotation. |
| 7 | +-- | |
| 8 | +-- | Usually, you would use `CofreeT` to annotate an existing AST with |
| 9 | +-- | metadata such as source locations, file names, etc. |
| 10 | + |
| 11 | +module Control.Comonad.Cofree.Trans where |
| 12 | + |
| 13 | +import Prelude |
| 14 | + |
| 15 | +import Control.Alternative (class Alt, class Plus, empty, (<|>)) |
| 16 | +import Control.Apply (lift2) |
| 17 | +import Control.Comonad (class Comonad, class Extend, extend, extract) |
| 18 | +import Control.Comonad.Cofree.Class (class ComonadCofree) |
| 19 | +import Control.Comonad.Env.Class (class ComonadAsk, ask) |
| 20 | +import Control.Comonad.Trans.Class (class ComonadTrans) |
| 21 | +import Control.Monad.Trans.Class (class MonadTrans) |
| 22 | +import Data.Bifunctor (bimap) |
| 23 | +import Data.Foldable (class Foldable, foldMap, foldlDefault, foldrDefault) |
| 24 | +import Data.Traversable (class Traversable, sequenceDefault, traverse) |
| 25 | +import Data.Tuple (Tuple(..)) |
| 26 | +import Data.Tuple as T |
| 27 | +import Effect.Aff.Class (class MonadAff, liftAff) |
| 28 | +import Effect.Class (class MonadEffect, liftEffect) |
| 29 | + |
| 30 | +-- | The cofree comonad transformer for the functor 'f'. |
| 31 | +newtype CofreeT f w a = CofreeT (Unit -> w (Tuple a (f (CofreeT f w a)))) |
| 32 | + |
| 33 | +-- | Construct a `CofreeT` from a lazy computation with an annotation 'a'. |
| 34 | +cofreeT |
| 35 | + :: forall f w a |
| 36 | + . (Unit -> w (Tuple a (f (CofreeT f w a)))) |
| 37 | + -> CofreeT f w a |
| 38 | +cofreeT = CofreeT |
| 39 | + |
| 40 | +-- | Construct a `CofreeT` from a computation with an annotation 'a'. |
| 41 | +cofreeT' |
| 42 | + :: forall f w a |
| 43 | + . w (Tuple a (f (CofreeT f w a))) |
| 44 | + -> CofreeT f w a |
| 45 | +cofreeT' t = CofreeT $ (\_ -> t) |
| 46 | + |
| 47 | +-- | Unpack `CofreeT` into the inner computation. |
| 48 | +runCofreeT :: forall f w a. CofreeT f w a -> w (Tuple a (f (CofreeT f w a))) |
| 49 | +runCofreeT (CofreeT f) = f unit |
| 50 | + |
| 51 | +-- | Obtain the annotation stored within a `CofreeT`. |
| 52 | +head :: forall f w a. Functor w => CofreeT f w a -> w a |
| 53 | +head = map T.fst <<< runCofreeT |
| 54 | + |
| 55 | +-- | Obtain the inner computation stored within a `CofreeT`. |
| 56 | +tail :: forall f w a. Functor w => CofreeT f w a -> w (f (CofreeT f w a)) |
| 57 | +tail = map T.snd <<< runCofreeT |
| 58 | + |
| 59 | +-- Note: This cannot be automatically derived because 'a' also appears in the |
| 60 | +-- 'fst' position of the inner Tuple. |
| 61 | +instance functorCofreeT :: (Functor w, Functor f) => Functor (CofreeT f w) where |
| 62 | + map f (CofreeT inner) = CofreeT $ map (map (bimap f (map (map f)))) inner |
| 63 | + |
| 64 | +instance applyCofreeT :: (Apply w, Apply f) => Apply (CofreeT f w) where |
| 65 | + apply (CofreeT innerF) (CofreeT inner) = |
| 66 | + CofreeT |
| 67 | + $ \_ -> |
| 68 | + go <$> innerF unit <*> inner unit |
| 69 | + where |
| 70 | + go (Tuple f nextF) (Tuple x nextX) = |
| 71 | + Tuple (f x) (lift2 (<*>) nextF nextX) |
| 72 | + |
| 73 | +instance applicativeCofreeT :: (Applicative w, Apply f, Plus f) => Applicative (CofreeT f w) where |
| 74 | + pure a = CofreeT $ \_ -> pure (Tuple a empty) |
| 75 | + |
| 76 | +instance bindCofreeT :: (Monad w, Alt f, Apply f) => Bind (CofreeT f w) where |
| 77 | + bind (CofreeT inner) f = |
| 78 | + CofreeT |
| 79 | + $ \_ -> do |
| 80 | + (Tuple a m) <- inner unit |
| 81 | + let (CofreeT next) = f a |
| 82 | + (Tuple b n) <- next unit |
| 83 | + pure $ Tuple b (n <|> map (_ >>= f) m) |
| 84 | + |
| 85 | +instance monadCofreeT :: (Monad w, Plus f, Apply f) => Monad (CofreeT f w) |
| 86 | + |
| 87 | +instance monadTransCofreeT :: Plus f => MonadTrans (CofreeT f) where |
| 88 | + lift = cofreeT' <<< map go |
| 89 | + where |
| 90 | + go x = Tuple x empty |
| 91 | + |
| 92 | +instance monadEffectCofreeT :: (MonadEffect w, Plus f, Apply f) => MonadEffect (CofreeT f w) where |
| 93 | + liftEffect eff = cofreeT' $ go <$> liftEffect eff |
| 94 | + where |
| 95 | + go a = Tuple a empty |
| 96 | + |
| 97 | +instance monadAffCofreeT :: (MonadAff w, Plus f, Apply f) => MonadAff (CofreeT f w) where |
| 98 | + liftAff aff = cofreeT' $ go <$> liftAff aff |
| 99 | + where |
| 100 | + go a = Tuple a empty |
| 101 | + |
| 102 | +instance comonadCofreeCofreeT :: (Comonad w, Functor f) => ComonadCofree f (CofreeT f w) where |
| 103 | + unwrapCofree = extract <<< tail |
| 104 | + |
| 105 | +instance comonadTransCofreeT :: ComonadTrans (CofreeT f) where |
| 106 | + lower = head |
| 107 | + |
| 108 | +instance comonadAskCofreeT :: (Functor f, ComonadAsk e w) => ComonadAsk e (CofreeT f w) where |
| 109 | + ask = ask <<< tail |
| 110 | + |
| 111 | +instance foldableCofreeT :: (Foldable w, Foldable f) => Foldable (CofreeT f w) where |
| 112 | + foldMap f (CofreeT inner) = foldMap go $ inner unit |
| 113 | + where |
| 114 | + go (Tuple a next) = f a <> foldMap (foldMap f) next |
| 115 | + |
| 116 | + foldr abb b = foldrDefault abb b |
| 117 | + |
| 118 | + foldl bab b = foldlDefault bab b |
| 119 | + |
| 120 | +instance traversableCofreeT :: (Traversable w, Traversable f) => Traversable (CofreeT f w) where |
| 121 | + traverse f (CofreeT inner) = |
| 122 | + cofreeT' <$> traverse go (inner unit) |
| 123 | + where |
| 124 | + go (Tuple a next) = Tuple <$> f a <*> traverse (traverse f) next |
| 125 | + |
| 126 | + sequence = sequenceDefault |
| 127 | + |
| 128 | +instance extendCofreeT :: (Comonad w, Functor f) => Extend (CofreeT f w) where |
| 129 | + extend f (CofreeT inner) = CofreeT $ \_ -> extend go (inner unit) |
| 130 | + where |
| 131 | + go w = Tuple (f $ cofreeT' w) $ extend f <$> T.snd (extract w) |
| 132 | + |
| 133 | +instance comonadCofreeT :: (Comonad w, Functor f) => Comonad (CofreeT f w) where |
| 134 | + extract = extract <<< head |
| 135 | + |
| 136 | +-- | 'hoist' the effect type using a natural transform. |
| 137 | +hoistCofreeT :: forall f w u a. Functor f => Functor u => (w ~> u) -> CofreeT f w a -> CofreeT f u a |
| 138 | +hoistCofreeT nm = bimapCofreeT identity nm |
| 139 | + |
| 140 | +-- | 'interpret' the inner functor using a natural transform. |
| 141 | +interpretCofreeT :: forall f g w a. Functor g => Functor w => (f ~> g) -> CofreeT f w a -> CofreeT g w a |
| 142 | +interpretCofreeT nf = bimapCofreeT nf identity |
| 143 | + |
| 144 | +-- | Both 'interpret' and 'hoist' the inner functor as well as the effect using natural transforms. |
| 145 | +bimapCofreeT :: forall f g w u a. Functor u => Functor g => (f ~> g) -> (w ~> u) -> CofreeT f w a -> CofreeT g u a |
| 146 | +bimapCofreeT nf nm (CofreeT inner) = CofreeT $ (map (map (map go))) $ map nm inner |
| 147 | + where |
| 148 | + go :: f (CofreeT f w a) -> g (CofreeT g u a) |
| 149 | + go = map (bimapCofreeT nf nm) <<< nf |
0 commit comments