packages feed

tangle-0.1: src/Control/Monad/Tangle.hs

{-# LANGUAGE RankNTypes, LambdaCase, DeriveFunctor, BangPatterns #-}
module Control.Monad.Tangle
  (TangleFT(..), hitchF
  , evalTangleFT
  , liftTangles
  , blank
  , hitch
  , gather
  , TangleF
  , evalTangleF
  , TangleT
  , evalTangleT
  , Tangle
  , evalTangle
  ) where

import Barbies
import Control.Applicative
import Control.Monad.Trans.Class
import Control.Monad.IO.Class
import Data.Functor.Identity
import Data.Functor.Compose

-- | 'TangleFT' is a higher-kinded heterogeneous memoisation monad transformer.
-- @t@ represents the shape of the underlying data structure, and @f@ is the wrapper type of each field.
-- This monad represents computations that depend on the contents of @t f@.
newtype TangleFT t f m a = TangleFT
  { runTangleFT :: t (Compose (TangleFT t f m) f)
    -> t (Compose Maybe f)
    -> m (t (Compose Maybe f), a) }
  deriving Functor

instance Monad m => Applicative (TangleFT t f m) where
  pure a = TangleFT $ \_ mem -> pure (mem, a)
  TangleFT m <*> TangleFT n = TangleFT $ \ts mem -> m ts mem
    >>= \(mem', f) -> (\(mem'', a) -> (mem'', f a)) <$> n ts mem'
instance Monad m => Monad (TangleFT t f m) where
  TangleFT m >>= k = TangleFT $ \ts mem -> m ts mem >>= \(mem', a) -> runTangleFT (k a) ts mem'

instance (Monad m, Semigroup a) => Semigroup (TangleFT t f m a) where
  (<>) = liftA2 (<>)

instance (Monad m, Monoid a) => Monoid (TangleFT t f m a) where
  mempty = pure mempty

instance MonadTrans (TangleFT t f) where
  lift m = TangleFT $ \_ mem -> fmap ((,) mem) m

instance MonadIO m => MonadIO (TangleFT t f m) where
  liftIO m = TangleFT $ \_ mem -> fmap ((,) mem) (liftIO m)

-- | Collect all results in the tangle.
gather :: (TraversableB t, Monad m) => TangleFT t f m (t f)
gather = TangleFT $ \env prev -> runTangleFT (btraverse getCompose env) env prev

-- | Obtain a value from the tangle. The result gets memoised.
hitchF :: Monad m
  => (forall h g. Functor g => (h a -> g (h a)) -> t h -> g (t h)) -- ^ van Laarhoven lens
  -> TangleFT t f m (f a)
hitchF l = TangleFT $ \ts mem -> getConst $ flip l mem $ \case
  Compose (Just a) -> Const $ pure (mem, a)
  Compose Nothing -> Const
    $ fmap (\(mem', a) -> let !(Identity mem'') = l (const $ pure $ Compose $ Just a) mem' in (mem'', a))
    $ runTangleFT (getCompose $ getConst $ l Const ts) ts mem
{-# INLINE hitchF #-}

evalTangleFT :: (ApplicativeB t, Functor m) => TangleFT t f m a -> t (Compose (TangleFT t f m) f) -> m a
evalTangleFT m t = snd <$> runTangleFT m t blank
{-# INLINE evalTangleFT #-}

-- | Lift a collection of 'TangleT's so that it fits the argument of 'runTangleFT'.
liftTangles :: (FunctorB b, Functor m) => b (TangleT b m) -> b (Compose (TangleT b m) Identity)
liftTangles = bmap (Compose . fmap Identity)
{-# INLINE liftTangles #-}

-- | A product where all the elements are 'Compose' 'Nothing'
blank :: ApplicativeB b => b (Compose Maybe f)
blank = bpure $ Compose Nothing

-- | Bare version of 'TangleFT'
type TangleT t = TangleFT t Identity

-- | Non-transformer version of 'TangleFT'
type TangleF t f = TangleFT t f Identity

-- | Bare non-transformer tangle
type Tangle t = TangleFT t Identity Identity

-- | Bare variant of 'hitchF'
hitch :: Monad m
  => (forall h g. Functor g => (h a -> g (h a)) -> t h -> g (t h)) -- ^ van Laarhoven lens
  -> TangleT t m a
hitch l = runIdentity <$> hitchF l
{-# INLINE hitch #-}

evalTangleF :: ApplicativeB t => TangleF t f a -> t (Compose (TangleF t f) f) -> a
evalTangleF m t = snd $ runIdentity $ runTangleFT m t blank
{-# INLINE evalTangleF #-}

evalTangleT :: (Functor m, ApplicativeB t) => TangleT t m a -> t (TangleT t m) -> m a
evalTangleT m t = fmap snd $ runTangleFT m (liftTangles t) blank
{-# INLINE evalTangleT #-}

evalTangle :: (ApplicativeB t) => Tangle t a -> t (Tangle t) -> a
evalTangle m t = runIdentity $ evalTangleT m t
{-# INLINE evalTangle #-}