packages feed

functor-monad-0.1.1.0: src/FMonad/FFree.hs

{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

-- | Free 'FMonad'
module FMonad.FFree where

import Data.Functor.Day (Day (..), day, dap)
import FFunctor
import FMonad
import FStrong

-- | The free 'FMonad' for a @'FFunctor' ff@.  
data FFree ff g x = FPure (g x) | FFree (ff (FFree ff g) x)

deriving instance (Show (g a), Show (ff (FFree ff g) a)) => Show (FFree ff g a)

deriving instance (Eq (g a), Eq (ff (FFree ff g) a)) => Eq (FFree ff g a)

deriving instance (Ord (g a), Ord (ff (FFree ff g) a)) => Ord (FFree ff g a)

deriving instance (Functor g, Functor (ff (FFree ff g))) => Functor (FFree ff g)

deriving instance (Foldable g, Foldable (ff (FFree ff g))) => Foldable (FFree ff g)

deriving instance (Traversable g, Traversable (ff (FFree ff g))) => Traversable (FFree ff g)

instance (FFunctor ff) => FFunctor (FFree ff) where
  ffmap gh (FPure gx) = FPure (gh gx)
  ffmap gh (FFree fmx) = FFree (ffmap (ffmap gh) fmx)

instance (FFunctor ff) => FMonad (FFree ff) where
  fpure = FPure
  fbind k (FPure gx) = k gx
  fbind k (FFree fmmx) = FFree (ffmap (fbind k) fmmx)

instance (FStrong ff) => FStrong (FFree ff) where
  fstrength (Day ffg h op) = case ffg of
    FPure g -> FPure (Day g h op)
    FFree ffr -> FFree (ffmap fstrength $ fstrength (Day ffr h op))

fffmap :: forall ff gg h.
     (FFunctor ff, FFunctor gg, Functor h)
  => (forall h'. (Functor h') => ff h' ~> gg h')
  -> (FFree ff h ~> FFree gg h)
fffmap _ (FPure hx) = FPure hx
fffmap t (FFree ffm) = FFree $ ffmap (fffmap t) (t ffm)

-- | Iteratively fold a @FFree@ term down, given a way to fold one layer of @ff@. 
iter :: forall ff g. (FFunctor ff, Functor g) => (ff g ~> g) -> FFree ff g ~> g
iter step = go
  where
    go :: FFree ff g ~> g
    go (FPure gx) = gx
    go (FFree fmx) = step (ffmap go fmx)

-- | Fold a @FFree@ term to another @FMonad mm@.
foldFFree :: forall ff mm g. (FFunctor ff, FMonad mm, Functor g) => (forall h. Functor h => ff h ~> mm h) -> FFree ff g ~> mm g
foldFFree toMM = go
  where
    go :: FFree ff g ~> mm g
    go (FPure gx) = fpure gx
    go (FFree ftx) = fjoin (ffmap go (toMM ftx))

-- | @retract = 'foldFFree' id@
retract :: (FMonad ff, Functor g) => FFree ff g ~> ff g
retract = foldFFree id

instance (FStrong ff, Applicative g) => Applicative (FFree ff g) where
  pure = FPure . pure
  FPure gx <*> y = ffmap dap $ fstrength' (day gx y)
  FFree ffr <*> y = FFree $ innerAp ffr y

-- | Lift a single layer of @ff@ into @FFree ff@
liftF :: (FFunctor ff, Functor g) => ff g ~> FFree ff g
liftF fgx = FFree (ffmap FPure fgx)