{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} -- | Several extensions to Edward Kmett's recursion schemes package. The monadic -- recursion schemes and exotic recursion schemes should be stable, but the -- recursion schemes for interdependent data type (and their attendant -- typeclasses) are experimental. module Data.Functor.Foldable.Exotic ( -- * Classes SubHom (..) , SubType (..) , CoSubHom (..) , Dummy (..) -- * Monadic recursion schemes , cataM , anaM , hyloM -- * Recursion schemes for interdependent data types , dendro , dendroTri , symplecto , chema -- * Exotic recursion schemes , dicata , micro ) where import Control.Arrow import Control.Composition import Control.Monad import Data.Functor.Foldable -- | Class that yields g-algebra homomorphisms between mutually recursive types. class (Functor f, Functor g) => SubHom f g a b where -- | Homomorphism of g-algebras parametrized by an f-algebra homo :: (f a -> a) -> (g b -> b) -> (g b -> b) class SubType b where -- | Resolve nested functions. switch :: b -> b -- | Class that yields g-coalgebra homomorphisms between mutually recursive types. class (Functor f, Functor g) => CoSubHom f g a b where -- | Homomorphism of g-coalgebras paramterized by an f-coalgebra homoCo :: (a -> f a) -> (b -> g b) -> (b -> g b) -- | We need this class to make type resolution work. class Dummy t where dummy :: t --margaritari :: -- | Entangle two hylomorphisms. Not the same thing as a symplectomorphism from geometry. symplecto :: (SubHom g f b b, CoSubHom g f a a) => (g b -> b) -- ^ A g-algebra -> (a -> g a) -- ^ A g-coalgebra -> (f b -> b) -- ^ An f-algebra -> (a -> f a) -- ^ An f-coalgebra -> a -> b symplecto = homoCo -.* (flip . ((.) .* hylo .* homo)) -- FIXME what the fuck did I do here -- Entangle two anamorphisms. chema :: (CoSubHom (Base t) (Base t') a b, SubType b, Corecursive t') => t -- ^ dummy type -> (a -> Base t a) -- A (Base t)-coalgebra -> (b -> Base t' b) -- A (Base t')-coalgebra -> b -> t' chema = const (pseudoana .* homoCo) where pseudoana g = a where a = embed . fmap (a . switch) . g . switch -- better idea: have a function to lift any f-algebra into a (f . w)-algebra for w a comonad -- ℤ ∀ ∈ ≠ ≤ ≥ ⇒ → ∧ ∨ ¬ 𝔹 ≡ ∪ ⊕ ∅ -- -- | A dendromorphism entangles two catamorphisms dendro :: (SubHom (Base t) (Base t') a b, SubType b, Recursive t') => t -- ^ dummy type -> (Base t a -> a) -- ^ A (Base t)-algebra -> (Base t' b -> b) -- ^ A (Base t')-algebra -> t' -> b dendro = const (pseudocata .* homo) where pseudocata f = c where c = switch . f . fmap (switch . c) . project -- | Entangle three base functors. dendroTri :: (SubHom (Base t) (Base t') a b, SubType b, Recursive t', SubHom (Base t'') (Base t) c a, SubType a, Recursive t) => t -- ^ dummy type -> t'' -- ^ another dummy type -> (Base t'' c -> c) -- ^ A (Base t'')-algebra -> (Base t a -> a) -- A (Base t)-algebra -> (Base t' b -> b) -- A (Base t')-algebra -> t' -> b dendroTri = fmap const (switch .** homo -.* (fmap <$> dendro)) -- | Catamorphism collapsing along two data types simultaneously. Basically a fancy zygomorphism. dicata :: (Recursive a) => (Base a (b, a) -> b) -> (Base a (b, a) -> a) -> a -> b dicata = fst .** (cata .* (&&&)) -- | A micromorphism is an Elgot algebra specialized to unfolding. micro :: (Corecursive a) => (b -> Either a (Base a b)) -> b -> a micro = elgot embed -- | A monadic catamorphism cataM :: (Recursive t, Traversable (Base t), Monad m) => (Base t a -> m a) -> (t -> m a) cataM phi = fix (fmap (phi <=<) (project -.* mapM)) -- | A monadic anamorphism anaM :: (Corecursive t, Traversable (Base t), Monad m) => (a -> m (Base t a)) -> (a -> m t) anaM = fix (fmap embed .** ((=<<) .* fmap traverse >=> fmap)) -- | A monadic hylomorphism hyloM :: (Functor f, Monad m, Traversable f) => (f b -> m b) -> (a -> m (f a)) -> a -> m b hyloM = fix (fmap (`flip` id) (ap .* ((<=<) .** (liftM2 fmap (<=<) <$> (mapM .*)))))