recursion-schemes-5.0.2: Generalized bananas, lenses and barbed wire

Copyright(C) 2008-2015 Edward Kmett
LicenseBSD-style (see the file LICENSE)
MaintainerEdward Kmett <ekmett@gmail.com>
Stabilityexperimental
Portabilitynon-portable
Safe HaskellSafe
LanguageHaskell98

Data.Functor.Foldable

Contents

Description

 

Synopsis

Base functors for fixed points

type family Base t :: * -> * Source #

Obtain the base functor for a recursive datatype.

The core idea of this library is that instead of writing recursive functions on a recursive datatype, we prefer to write non-recursive functions on a related, non-recursive datatype we call the "base functor".

For example, [a] is a recursive type, and its corresponding base functor is ListF a:

data ListF a b = Nil | Cons a b
type instance Base [a] = ListF a

The relationship between those two types is that if we replace b with ListF a, we obtain a type which is isomorphic to [a].

Instances

type Base Natural Source # 
type Base [a] Source # 
type Base [a] = ListF a
type Base (Maybe a) Source # 
type Base (Maybe a) = Const * (Maybe a)
type Base (NonEmpty a) Source # 
type Base (NonEmpty a) = NonEmptyF a
type Base (Nu f) Source # 
type Base (Nu f) = f
type Base (Mu f) Source # 
type Base (Mu f) = f
type Base (Fix f) Source # 
type Base (Fix f) = f
type Base (Either a b) Source # 
type Base (Either a b) = Const * (Either a b)
type Base (Cofree f a) Source # 
type Base (Cofree f a) = CofreeF f a
type Base (F f a) Source # 
type Base (F f a) = FreeF f a
type Base (Free f a) Source # 
type Base (Free f a) = FreeF f a
type Base (FreeT f m a) Source # 
type Base (FreeT f m a) = Compose * * m (FreeF f a)
type Base (CofreeT f w a) Source # 
type Base (CofreeT f w a) = Compose * * w (CofreeF f a)

data ListF a b Source #

Base functor of [].

Constructors

Nil 
Cons a b 

Instances

Bitraversable ListF Source # 

Methods

bitraverse :: Applicative f => (a -> f c) -> (b -> f d) -> ListF a b -> f (ListF c d) #

Bifoldable ListF Source # 

Methods

bifold :: Monoid m => ListF m m -> m #

bifoldMap :: Monoid m => (a -> m) -> (b -> m) -> ListF a b -> m #

bifoldr :: (a -> c -> c) -> (b -> c -> c) -> c -> ListF a b -> c #

bifoldl :: (c -> a -> c) -> (c -> b -> c) -> c -> ListF a b -> c #

Bifunctor ListF Source # 

Methods

bimap :: (a -> b) -> (c -> d) -> ListF a c -> ListF b d #

first :: (a -> b) -> ListF a c -> ListF b c #

second :: (b -> c) -> ListF a b -> ListF a c #

Eq2 ListF Source # 

Methods

liftEq2 :: (a -> b -> Bool) -> (c -> d -> Bool) -> ListF a c -> ListF b d -> Bool #

Ord2 ListF Source # 

Methods

liftCompare2 :: (a -> b -> Ordering) -> (c -> d -> Ordering) -> ListF a c -> ListF b d -> Ordering #

Read2 ListF Source # 

Methods

liftReadsPrec2 :: (Int -> ReadS a) -> ReadS [a] -> (Int -> ReadS b) -> ReadS [b] -> Int -> ReadS (ListF a b) #

liftReadList2 :: (Int -> ReadS a) -> ReadS [a] -> (Int -> ReadS b) -> ReadS [b] -> ReadS [ListF a b] #

liftReadPrec2 :: ReadPrec a -> ReadPrec [a] -> ReadPrec b -> ReadPrec [b] -> ReadPrec (ListF a b) #

liftReadListPrec2 :: ReadPrec a -> ReadPrec [a] -> ReadPrec b -> ReadPrec [b] -> ReadPrec [ListF a b] #

Show2 ListF Source # 

Methods

liftShowsPrec2 :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> (Int -> b -> ShowS) -> ([b] -> ShowS) -> Int -> ListF a b -> ShowS #

liftShowList2 :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> (Int -> b -> ShowS) -> ([b] -> ShowS) -> [ListF a b] -> ShowS #

Functor (ListF a) Source # 

Methods

fmap :: (a -> b) -> ListF a a -> ListF a b #

(<$) :: a -> ListF a b -> ListF a a #

Foldable (ListF a) Source # 

Methods

fold :: Monoid m => ListF a m -> m #

foldMap :: Monoid m => (a -> m) -> ListF a a -> m #

foldr :: (a -> b -> b) -> b -> ListF a a -> b #

foldr' :: (a -> b -> b) -> b -> ListF a a -> b #

foldl :: (b -> a -> b) -> b -> ListF a a -> b #

foldl' :: (b -> a -> b) -> b -> ListF a a -> b #

foldr1 :: (a -> a -> a) -> ListF a a -> a #

foldl1 :: (a -> a -> a) -> ListF a a -> a #

toList :: ListF a a -> [a] #

null :: ListF a a -> Bool #

length :: ListF a a -> Int #

elem :: Eq a => a -> ListF a a -> Bool #

maximum :: Ord a => ListF a a -> a #

minimum :: Ord a => ListF a a -> a #

sum :: Num a => ListF a a -> a #

product :: Num a => ListF a a -> a #

Traversable (ListF a) Source # 

Methods

traverse :: Applicative f => (a -> f b) -> ListF a a -> f (ListF a b) #

sequenceA :: Applicative f => ListF a (f a) -> f (ListF a a) #

mapM :: Monad m => (a -> m b) -> ListF a a -> m (ListF a b) #

sequence :: Monad m => ListF a (m a) -> m (ListF a a) #

Eq a => Eq1 (ListF a) Source # 

Methods

liftEq :: (a -> b -> Bool) -> ListF a a -> ListF a b -> Bool #

Ord a => Ord1 (ListF a) Source # 

Methods

liftCompare :: (a -> b -> Ordering) -> ListF a a -> ListF a b -> Ordering #

Read a => Read1 (ListF a) Source # 

Methods

liftReadsPrec :: (Int -> ReadS a) -> ReadS [a] -> Int -> ReadS (ListF a a) #

liftReadList :: (Int -> ReadS a) -> ReadS [a] -> ReadS [ListF a a] #

liftReadPrec :: ReadPrec a -> ReadPrec [a] -> ReadPrec (ListF a a) #

liftReadListPrec :: ReadPrec a -> ReadPrec [a] -> ReadPrec [ListF a a] #

Show a => Show1 (ListF a) Source # 

Methods

liftShowsPrec :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> Int -> ListF a a -> ShowS #

liftShowList :: (Int -> a -> ShowS) -> ([a] -> ShowS) -> [ListF a a] -> ShowS #

Generic1 * (ListF a) Source # 

Associated Types

type Rep1 (ListF a) (f :: ListF a -> *) :: k -> * #

Methods

from1 :: f a -> Rep1 (ListF a) f a #

to1 :: Rep1 (ListF a) f a -> f a #

(Eq b, Eq a) => Eq (ListF a b) Source # 

Methods

(==) :: ListF a b -> ListF a b -> Bool #

(/=) :: ListF a b -> ListF a b -> Bool #

(Ord b, Ord a) => Ord (ListF a b) Source # 

Methods

compare :: ListF a b -> ListF a b -> Ordering #

(<) :: ListF a b -> ListF a b -> Bool #

(<=) :: ListF a b -> ListF a b -> Bool #

(>) :: ListF a b -> ListF a b -> Bool #

(>=) :: ListF a b -> ListF a b -> Bool #

max :: ListF a b -> ListF a b -> ListF a b #

min :: ListF a b -> ListF a b -> ListF a b #

(Read b, Read a) => Read (ListF a b) Source # 
(Show b, Show a) => Show (ListF a b) Source # 

Methods

showsPrec :: Int -> ListF a b -> ShowS #

show :: ListF a b -> String #

showList :: [ListF a b] -> ShowS #

Generic (ListF a b) Source # 

Associated Types

type Rep (ListF a b) :: * -> * #

Methods

from :: ListF a b -> Rep (ListF a b) x #

to :: Rep (ListF a b) x -> ListF a b #

type Rep1 * (ListF a) Source # 
type Rep1 * (ListF a) = D1 * (MetaData "ListF" "Data.Functor.Foldable" "recursion-schemes-5.0.2-mxSLVta5J5BjXwPP4dt83" False) ((:+:) * (C1 * (MetaCons "Nil" PrefixI False) (U1 *)) (C1 * (MetaCons "Cons" PrefixI False) ((:*:) * (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 * a)) (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) Par1))))
type Rep (ListF a b) Source # 
type Rep (ListF a b) = D1 * (MetaData "ListF" "Data.Functor.Foldable" "recursion-schemes-5.0.2-mxSLVta5J5BjXwPP4dt83" False) ((:+:) * (C1 * (MetaCons "Nil" PrefixI False) (U1 *)) (C1 * (MetaCons "Cons" PrefixI False) ((:*:) * (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 * a)) (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 * b)))))

Fixed points

newtype Fix f Source #

Construct a recursive datatype from a base functor.

For example, [String] and Fix (ListF String) are isomorphic:

["foo", "bar"]
Fix (Cons "foo" (Fix (Cons "bar" (Fix Nil))))

Unlike Mu and Nu, this representation is concrete, so we can pattern-match on the constructors of f.

Constructors

Fix (f (Fix f)) 

Instances

Eq1 f => Eq (Fix f) Source # 

Methods

(==) :: Fix f -> Fix f -> Bool #

(/=) :: Fix f -> Fix f -> Bool #

(Typeable (* -> *) f, Data (f (Fix f))) => Data (Fix f) Source # 

Methods

gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> Fix f -> c (Fix f) #

gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c (Fix f) #

toConstr :: Fix f -> Constr #

dataTypeOf :: Fix f -> DataType #

dataCast1 :: Typeable (* -> *) t => (forall d. Data d => c (t d)) -> Maybe (c (Fix f)) #

dataCast2 :: Typeable (* -> * -> *) t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c (Fix f)) #

gmapT :: (forall b. Data b => b -> b) -> Fix f -> Fix f #

gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Fix f -> r #

gmapQr :: (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Fix f -> r #

gmapQ :: (forall d. Data d => d -> u) -> Fix f -> [u] #

gmapQi :: Int -> (forall d. Data d => d -> u) -> Fix f -> u #

gmapM :: Monad m => (forall d. Data d => d -> m d) -> Fix f -> m (Fix f) #

gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> Fix f -> m (Fix f) #

gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> Fix f -> m (Fix f) #

Ord1 f => Ord (Fix f) Source # 

Methods

compare :: Fix f -> Fix f -> Ordering #

(<) :: Fix f -> Fix f -> Bool #

(<=) :: Fix f -> Fix f -> Bool #

(>) :: Fix f -> Fix f -> Bool #

(>=) :: Fix f -> Fix f -> Bool #

max :: Fix f -> Fix f -> Fix f #

min :: Fix f -> Fix f -> Fix f #

Read1 f => Read (Fix f) Source # 
Show1 f => Show (Fix f) Source # 

Methods

showsPrec :: Int -> Fix f -> ShowS #

show :: Fix f -> String #

showList :: [Fix f] -> ShowS #

Functor f => Corecursive (Fix f) Source # 

Methods

embed :: Base (Fix f) (Fix f) -> Fix f Source #

ana :: (a -> Base (Fix f) a) -> a -> Fix f Source #

apo :: (a -> Base (Fix f) (Either (Fix f) a)) -> a -> Fix f Source #

postpro :: Recursive (Fix f) => (forall b. Base (Fix f) b -> Base (Fix f) b) -> (a -> Base (Fix f) a) -> a -> Fix f Source #

gpostpro :: (Recursive (Fix f), Monad m) => (forall b. m (Base (Fix f) b) -> Base (Fix f) (m b)) -> (forall c. Base (Fix f) c -> Base (Fix f) c) -> (a -> Base (Fix f) (m a)) -> a -> Fix f Source #

Functor f => Recursive (Fix f) Source # 

Methods

project :: Fix f -> Base (Fix f) (Fix f) Source #

cata :: (Base (Fix f) a -> a) -> Fix f -> a Source #

para :: (Base (Fix f) (Fix f, a) -> a) -> Fix f -> a Source #

gpara :: (Corecursive (Fix f), Comonad w) => (forall b. Base (Fix f) (w b) -> w (Base (Fix f) b)) -> (Base (Fix f) (EnvT (Fix f) w a) -> a) -> Fix f -> a Source #

prepro :: Corecursive (Fix f) => (forall b. Base (Fix f) b -> Base (Fix f) b) -> (Base (Fix f) a -> a) -> Fix f -> a Source #

gprepro :: (Corecursive (Fix f), Comonad w) => (forall b. Base (Fix f) (w b) -> w (Base (Fix f) b)) -> (forall c. Base (Fix f) c -> Base (Fix f) c) -> (Base (Fix f) (w a) -> a) -> Fix f -> a Source #

type Base (Fix f) Source # 
type Base (Fix f) = f

unfix :: Fix f -> f (Fix f) Source #

newtype Mu f Source #

The least fixed point of f, in the sense that if we did not have general recursion, we would be forced to use the f a -> a argument a finite number of times and so we could only construct finite values. Since we do have general recursion, Fix, Mu and Nu are all equivalent.

For example, Fix (ListF String) and Mu (ListF String) are isomorphic:

Fix         (Cons "foo" (Fix (Cons "bar" (Fix Nil))))
Mu (\f -> f (Cons "foo" (f   (Cons "bar" (f   Nil)))))

Constructors

Mu (forall a. (f a -> a) -> a) 

Instances

(Functor f, Eq1 f) => Eq (Mu f) Source # 

Methods

(==) :: Mu f -> Mu f -> Bool #

(/=) :: Mu f -> Mu f -> Bool #

(Functor f, Ord1 f) => Ord (Mu f) Source # 

Methods

compare :: Mu f -> Mu f -> Ordering #

(<) :: Mu f -> Mu f -> Bool #

(<=) :: Mu f -> Mu f -> Bool #

(>) :: Mu f -> Mu f -> Bool #

(>=) :: Mu f -> Mu f -> Bool #

max :: Mu f -> Mu f -> Mu f #

min :: Mu f -> Mu f -> Mu f #

(Functor f, Read1 f) => Read (Mu f) Source # 
(Functor f, Show1 f) => Show (Mu f) Source # 

Methods

showsPrec :: Int -> Mu f -> ShowS #

show :: Mu f -> String #

showList :: [Mu f] -> ShowS #

Functor f => Corecursive (Mu f) Source # 

Methods

embed :: Base (Mu f) (Mu f) -> Mu f Source #

ana :: (a -> Base (Mu f) a) -> a -> Mu f Source #

apo :: (a -> Base (Mu f) (Either (Mu f) a)) -> a -> Mu f Source #

postpro :: Recursive (Mu f) => (forall b. Base (Mu f) b -> Base (Mu f) b) -> (a -> Base (Mu f) a) -> a -> Mu f Source #

gpostpro :: (Recursive (Mu f), Monad m) => (forall b. m (Base (Mu f) b) -> Base (Mu f) (m b)) -> (forall c. Base (Mu f) c -> Base (Mu f) c) -> (a -> Base (Mu f) (m a)) -> a -> Mu f Source #

Functor f => Recursive (Mu f) Source # 

Methods

project :: Mu f -> Base (Mu f) (Mu f) Source #

cata :: (Base (Mu f) a -> a) -> Mu f -> a Source #

para :: (Base (Mu f) (Mu f, a) -> a) -> Mu f -> a Source #

gpara :: (Corecursive (Mu f), Comonad w) => (forall b. Base (Mu f) (w b) -> w (Base (Mu f) b)) -> (Base (Mu f) (EnvT (Mu f) w a) -> a) -> Mu f -> a Source #

prepro :: Corecursive (Mu f) => (forall b. Base (Mu f) b -> Base (Mu f) b) -> (Base (Mu f) a -> a) -> Mu f -> a Source #

gprepro :: (Corecursive (Mu f), Comonad w) => (forall b. Base (Mu f) (w b) -> w (Base (Mu f) b)) -> (forall c. Base (Mu f) c -> Base (Mu f) c) -> (Base (Mu f) (w a) -> a) -> Mu f -> a Source #

type Base (Mu f) Source # 
type Base (Mu f) = f

data Nu f where Source #

The greatest fixed point of f, in the sense that even if we did not have general recursion, we could still describe an infinite list by defining an a -> ListF Int a function which always returns a Cons. Since we do have general recursion, Fix, Mu and Nu are all equivalent.

For example, Fix (ListF String) and Nu (ListF String) are isomorphic:

Fix            (Cons "foo" (Fix   (Cons "bar" (Fix    Nil))))
Nu (\case {0 -> Cons "foo" 1; 1 -> Cons "bar" 2; _ -> Nil}) 0

Constructors

Nu :: (a -> f a) -> a -> Nu f 

Instances

(Functor f, Eq1 f) => Eq (Nu f) Source # 

Methods

(==) :: Nu f -> Nu f -> Bool #

(/=) :: Nu f -> Nu f -> Bool #

(Functor f, Ord1 f) => Ord (Nu f) Source # 

Methods

compare :: Nu f -> Nu f -> Ordering #

(<) :: Nu f -> Nu f -> Bool #

(<=) :: Nu f -> Nu f -> Bool #

(>) :: Nu f -> Nu f -> Bool #

(>=) :: Nu f -> Nu f -> Bool #

max :: Nu f -> Nu f -> Nu f #

min :: Nu f -> Nu f -> Nu f #

(Functor f, Read1 f) => Read (Nu f) Source # 
(Functor f, Show1 f) => Show (Nu f) Source # 

Methods

showsPrec :: Int -> Nu f -> ShowS #

show :: Nu f -> String #

showList :: [Nu f] -> ShowS #

Functor f => Corecursive (Nu f) Source # 

Methods

embed :: Base (Nu f) (Nu f) -> Nu f Source #

ana :: (a -> Base (Nu f) a) -> a -> Nu f Source #

apo :: (a -> Base (Nu f) (Either (Nu f) a)) -> a -> Nu f Source #

postpro :: Recursive (Nu f) => (forall b. Base (Nu f) b -> Base (Nu f) b) -> (a -> Base (Nu f) a) -> a -> Nu f Source #

gpostpro :: (Recursive (Nu f), Monad m) => (forall b. m (Base (Nu f) b) -> Base (Nu f) (m b)) -> (forall c. Base (Nu f) c -> Base (Nu f) c) -> (a -> Base (Nu f) (m a)) -> a -> Nu f Source #

Functor f => Recursive (Nu f) Source # 

Methods

project :: Nu f -> Base (Nu f) (Nu f) Source #

cata :: (Base (Nu f) a -> a) -> Nu f -> a Source #

para :: (Base (Nu f) (Nu f, a) -> a) -> Nu f -> a Source #

gpara :: (Corecursive (Nu f), Comonad w) => (forall b. Base (Nu f) (w b) -> w (Base (Nu f) b)) -> (Base (Nu f) (EnvT (Nu f) w a) -> a) -> Nu f -> a Source #

prepro :: Corecursive (Nu f) => (forall b. Base (Nu f) b -> Base (Nu f) b) -> (Base (Nu f) a -> a) -> Nu f -> a Source #

gprepro :: (Corecursive (Nu f), Comonad w) => (forall b. Base (Nu f) (w b) -> w (Base (Nu f) b)) -> (forall c. Base (Nu f) c -> Base (Nu f) c) -> (Base (Nu f) (w a) -> a) -> Nu f -> a Source #

type Base (Nu f) Source # 
type Base (Nu f) = f

Folding

class Functor (Base t) => Recursive t where Source #

A recursive datatype which can be unrolled one recursion layer at a time.

For example, a value of type [a] can be unrolled into a ListF a [a]. If that unrolled value is a Cons, it contains another [a] which can be unrolled as well, and so on.

Typically, Recursive types also have a Corecursive instance, in which case project and embed are inverses.

Minimal complete definition

project

Methods

project :: t -> Base t t Source #

Unroll a single recursion layer.

>>> project [1,2,3]
Cons 1 [2,3]

cata Source #

Arguments

:: (Base t a -> a)

a (Base t)-algebra

-> t

fixed point

-> a

result

A generalization of foldr. The elements of the base functor, called the "recursive positions", give the result of folding the sub-tree at that position.

-- |
-- >>> sum [1,2,3]
-- 6
sum :: [Int] -> Int
sum = cata sumF

sumF :: ListF Int Int -> Int
sumF Nil          = 0
sumF (Cons x acc) = x + acc

para :: (Base t (t, a) -> a) -> t -> a Source #

A variant of cata in which recursive positions also include the original sub-tree, in addition to the result of folding that sub-tree.

Useful when matching on a pattern which spans more than one recursion step:

-- |
-- >>> splitAtCommaSpace "one, two, three"
-- Just ("one","two, three")
splitAtCommaSpace :: String -> Maybe (String,String)
splitAtCommaSpace = para splitAtCommaSpaceF

splitAtCommaSpaceF :: ListF Char (String, Maybe (String,String))
                   -> Maybe (String,String)
splitAtCommaSpaceF (Cons ',' (' ':ys, _))     = Just ([], ys)
splitAtCommaSpaceF (Cons x (_, Just (xs,ys))) = Just (x:xs, ys)
splitAtCommaSpaceF _                          = Nothing

gpara :: (Corecursive t, Comonad w) => (forall b. Base t (w b) -> w (Base t b)) -> (Base t (EnvT t w a) -> a) -> t -> a Source #

A generalized paramorphism. Like para, each recursive position gives the result of the fold on its sub-tree and also the sub-tree itself. Depending on the distributive law, more information about that sub-tree may also be provided.

For example, we could build a "zygomorphic paramorphism", in which the result of a cata is also provided:

-- |
-- >>> calc "subtract 2; multiply by 2; add 1" <*> pure 42
-- Just 81
calc :: String -> Maybe (Int -> Int)
calc = gpara (distZygo parseNumberF) calcF

parseDigit :: Char -> Maybe Int
parseDigit c = (ord c - ord '0') <$ guard (c `elem` ['0'..'9'])

parseNumberF :: ListF Char (Maybe Int) -> Maybe Int
parseNumberF Nil              = Nothing
parseNumberF (Cons ';' _)     = Nothing
parseNumberF (Cons c Nothing) = parseDigit c
parseNumberF (Cons c maybeY)
  | c `elem` ['0'..'9'] = (\x y -> 10 * x + y) <$> parseDigit c <*> maybeY
  | otherwise           = maybeY

calcF :: ListF Char (EnvT String
                          ((,) (Maybe Int))
                          (Maybe (Int -> Int)))
      -> Maybe (Int -> Int)
calcF Nil = Just id
calcF (Cons c (EnvT cs (maybeX,maybeF)))
  | "add "         `isPrefixOf` (c:cs) = (\f x -> f . (+ x))        <$> maybeF <*> maybeX
  | "subtract "    `isPrefixOf` (c:cs) = (\f x -> f . (subtract x)) <$> maybeF <*> maybeX
  | "multiply by " `isPrefixOf` (c:cs) = (\f x -> f . (* x))        <$> maybeF <*> maybeX
  | otherwise                          = maybeF

prepro :: Corecursive t => (forall b. Base t b -> Base t b) -> (Base t a -> a) -> t -> a Source #

Fokkinga's prepromorphism. Applies the natural transformation n times to the base functors at depth n, then collapses the results using a cata. The outermost base functor has depth zero.

Useful for indenting sub-trees in a pretty-printer:

-- |
-- >>> putStr $ drawList ["foo","bar","baz"]
-- foo
--   bar
--     baz
drawList :: [String] -> String
drawList = prepro indent drawListF

indent :: ListF String a -> ListF String a
indent Nil        = Nil
indent (Cons s x) = Cons ("  " ++ s) x

drawListF :: ListF String String -> String
drawListF Nil               = ""
drawListF (Cons line lines) = line ++ "\n" ++ lines

gprepro :: (Corecursive t, Comonad w) => (forall b. Base t (w b) -> w (Base t b)) -> (forall c. Base t c -> Base t c) -> (Base t (w a) -> a) -> t -> a Source #

A generalized prepromorphism. Like prepro, the natural transformation is applied n times to the base functors at depth n. The results are then folded using the operation corresponding to the given distributive law.

For example, we could build a "zygomorphic prepromorphism", which folds the results using a zygo:

type Tree a = Fix (TreeF a)
data TreeF a b = Leaf a | Branch b b
  deriving Functor

leaf :: a -> Tree a
leaf = Fix . Leaf

branch :: Tree a -> Tree a -> Tree a
branch l r = Fix $ Branch l r

-- |
-- >>> let tree = ((leaf "0.1.1.1" `branch` leaf "0.1.1.2") `branch` leaf "0.1.2") `branch` (leaf "0.2.1" `branch` leaf "0.2.2")
-- >>> putStrLn $ drawTree tree
-- 0.
--   0.1.
--     0.1.1.
--       0.1.1.1
--       0.1.1.2
--     0.1.2
--   0.2.
--     0.2.1
--     0.2.2
drawTree :: Tree String -> String
drawTree = gprepro (distZygo mergeHeaders) indent drawTreeF

indent :: TreeF String a -> TreeF String a
indent (Leaf s) = Leaf ("  " ++ s)
indent x        = x

mergeHeaders :: TreeF String String -> String
mergeHeaders (Leaf s) = s
mergeHeaders (Branch s1 s2)
  = drop 2
  $ takeWhile (/= '\0')
  $ zipWith (\c1 c2 -> if c1 == c2 then c1 else '\0') s1 s2

drawTreeF :: TreeF String (String, String) -> String
drawTreeF (Leaf s) = s
drawTreeF (Branch (header1, s1) (header2, s2))
  = mergeHeaders (Branch header1 header2) ++ "\n"
 ++ s1 ++ "\n"
 ++ s2

Instances

Recursive Natural Source # 

Methods

project :: Natural -> Base Natural Natural Source #

cata :: (Base Natural a -> a) -> Natural -> a Source #

para :: (Base Natural (Natural, a) -> a) -> Natural -> a Source #

gpara :: (Corecursive Natural, Comonad w) => (forall b. Base Natural (w b) -> w (Base Natural b)) -> (Base Natural (EnvT Natural w a) -> a) -> Natural -> a Source #

prepro :: Corecursive Natural => (forall b. Base Natural b -> Base Natural b) -> (Base Natural a -> a) -> Natural -> a Source #

gprepro :: (Corecursive Natural, Comonad w) => (forall b. Base Natural (w b) -> w (Base Natural b)) -> (forall c. Base Natural c -> Base Natural c) -> (Base Natural (w a) -> a) -> Natural -> a Source #

Recursive [a] Source # 

Methods

project :: [a] -> Base [a] [a] Source #

cata :: (Base [a] a -> a) -> [a] -> a Source #

para :: (Base [a] ([a], a) -> a) -> [a] -> a Source #

gpara :: (Corecursive [a], Comonad w) => (forall b. Base [a] (w b) -> w (Base [a] b)) -> (Base [a] (EnvT [a] w a) -> a) -> [a] -> a Source #

prepro :: Corecursive [a] => (forall b. Base [a] b -> Base [a] b) -> (Base [a] a -> a) -> [a] -> a Source #

gprepro :: (Corecursive [a], Comonad w) => (forall b. Base [a] (w b) -> w (Base [a] b)) -> (forall c. Base [a] c -> Base [a] c) -> (Base [a] (w a) -> a) -> [a] -> a Source #

Recursive (Maybe a) Source # 

Methods

project :: Maybe a -> Base (Maybe a) (Maybe a) Source #

cata :: (Base (Maybe a) a -> a) -> Maybe a -> a Source #

para :: (Base (Maybe a) (Maybe a, a) -> a) -> Maybe a -> a Source #

gpara :: (Corecursive (Maybe a), Comonad w) => (forall b. Base (Maybe a) (w b) -> w (Base (Maybe a) b)) -> (Base (Maybe a) (EnvT (Maybe a) w a) -> a) -> Maybe a -> a Source #

prepro :: Corecursive (Maybe a) => (forall b. Base (Maybe a) b -> Base (Maybe a) b) -> (Base (Maybe a) a -> a) -> Maybe a -> a Source #

gprepro :: (Corecursive (Maybe a), Comonad w) => (forall b. Base (Maybe a) (w b) -> w (Base (Maybe a) b)) -> (forall c. Base (Maybe a) c -> Base (Maybe a) c) -> (Base (Maybe a) (w a) -> a) -> Maybe a -> a Source #

Recursive (NonEmpty a) Source # 

Methods

project :: NonEmpty a -> Base (NonEmpty a) (NonEmpty a) Source #

cata :: (Base (NonEmpty a) a -> a) -> NonEmpty a -> a Source #

para :: (Base (NonEmpty a) (NonEmpty a, a) -> a) -> NonEmpty a -> a Source #

gpara :: (Corecursive (NonEmpty a), Comonad w) => (forall b. Base (NonEmpty a) (w b) -> w (Base (NonEmpty a) b)) -> (Base (NonEmpty a) (EnvT (NonEmpty a) w a) -> a) -> NonEmpty a -> a Source #

prepro :: Corecursive (NonEmpty a) => (forall b. Base (NonEmpty a) b -> Base (NonEmpty a) b) -> (Base (NonEmpty a) a -> a) -> NonEmpty a -> a Source #

gprepro :: (Corecursive (NonEmpty a), Comonad w) => (forall b. Base (NonEmpty a) (w b) -> w (Base (NonEmpty a) b)) -> (forall c. Base (NonEmpty a) c -> Base (NonEmpty a) c) -> (Base (NonEmpty a) (w a) -> a) -> NonEmpty a -> a Source #

Functor f => Recursive (Nu f) Source # 

Methods

project :: Nu f -> Base (Nu f) (Nu f) Source #

cata :: (Base (Nu f) a -> a) -> Nu f -> a Source #

para :: (Base (Nu f) (Nu f, a) -> a) -> Nu f -> a Source #

gpara :: (Corecursive (Nu f), Comonad w) => (forall b. Base (Nu f) (w b) -> w (Base (Nu f) b)) -> (Base (Nu f) (EnvT (Nu f) w a) -> a) -> Nu f -> a Source #

prepro :: Corecursive (Nu f) => (forall b. Base (Nu f) b -> Base (Nu f) b) -> (Base (Nu f) a -> a) -> Nu f -> a Source #

gprepro :: (Corecursive (Nu f), Comonad w) => (forall b. Base (Nu f) (w b) -> w (Base (Nu f) b)) -> (forall c. Base (Nu f) c -> Base (Nu f) c) -> (Base (Nu f) (w a) -> a) -> Nu f -> a Source #

Functor f => Recursive (Mu f) Source # 

Methods

project :: Mu f -> Base (Mu f) (Mu f) Source #

cata :: (Base (Mu f) a -> a) -> Mu f -> a Source #

para :: (Base (Mu f) (Mu f, a) -> a) -> Mu f -> a Source #

gpara :: (Corecursive (Mu f), Comonad w) => (forall b. Base (Mu f) (w b) -> w (Base (Mu f) b)) -> (Base (Mu f) (EnvT (Mu f) w a) -> a) -> Mu f -> a Source #

prepro :: Corecursive (Mu f) => (forall b. Base (Mu f) b -> Base (Mu f) b) -> (Base (Mu f) a -> a) -> Mu f -> a Source #

gprepro :: (Corecursive (Mu f), Comonad w) => (forall b. Base (Mu f) (w b) -> w (Base (Mu f) b)) -> (forall c. Base (Mu f) c -> Base (Mu f) c) -> (Base (Mu f) (w a) -> a) -> Mu f -> a Source #

Functor f => Recursive (Fix f) Source # 

Methods

project :: Fix f -> Base (Fix f) (Fix f) Source #

cata :: (Base (Fix f) a -> a) -> Fix f -> a Source #

para :: (Base (Fix f) (Fix f, a) -> a) -> Fix f -> a Source #

gpara :: (Corecursive (Fix f), Comonad w) => (forall b. Base (Fix f) (w b) -> w (Base (Fix f) b)) -> (Base (Fix f) (EnvT (Fix f) w a) -> a) -> Fix f -> a Source #

prepro :: Corecursive (Fix f) => (forall b. Base (Fix f) b -> Base (Fix f) b) -> (Base (Fix f) a -> a) -> Fix f -> a Source #

gprepro :: (Corecursive (Fix f), Comonad w) => (forall b. Base (Fix f) (w b) -> w (Base (Fix f) b)) -> (forall c. Base (Fix f) c -> Base (Fix f) c) -> (Base (Fix f) (w a) -> a) -> Fix f -> a Source #

Recursive (Either a b) Source # 

Methods

project :: Either a b -> Base (Either a b) (Either a b) Source #

cata :: (Base (Either a b) a -> a) -> Either a b -> a Source #

para :: (Base (Either a b) (Either a b, a) -> a) -> Either a b -> a Source #

gpara :: (Corecursive (Either a b), Comonad w) => (forall c. Base (Either a b) (w c) -> w (Base (Either a b) c)) -> (Base (Either a b) (EnvT (Either a b) w a) -> a) -> Either a b -> a Source #

prepro :: Corecursive (Either a b) => (forall c. Base (Either a b) c -> Base (Either a b) c) -> (Base (Either a b) a -> a) -> Either a b -> a Source #

gprepro :: (Corecursive (Either a b), Comonad w) => (forall c. Base (Either a b) (w c) -> w (Base (Either a b) c)) -> (forall c. Base (Either a b) c -> Base (Either a b) c) -> (Base (Either a b) (w a) -> a) -> Either a b -> a Source #

Functor f => Recursive (Cofree f a) Source # 

Methods

project :: Cofree f a -> Base (Cofree f a) (Cofree f a) Source #

cata :: (Base (Cofree f a) a -> a) -> Cofree f a -> a Source #

para :: (Base (Cofree f a) (Cofree f a, a) -> a) -> Cofree f a -> a Source #

gpara :: (Corecursive (Cofree f a), Comonad w) => (forall b. Base (Cofree f a) (w b) -> w (Base (Cofree f a) b)) -> (Base (Cofree f a) (EnvT (Cofree f a) w a) -> a) -> Cofree f a -> a Source #

prepro :: Corecursive (Cofree f a) => (forall b. Base (Cofree f a) b -> Base (Cofree f a) b) -> (Base (Cofree f a) a -> a) -> Cofree f a -> a Source #

gprepro :: (Corecursive (Cofree f a), Comonad w) => (forall b. Base (Cofree f a) (w b) -> w (Base (Cofree f a) b)) -> (forall c. Base (Cofree f a) c -> Base (Cofree f a) c) -> (Base (Cofree f a) (w a) -> a) -> Cofree f a -> a Source #

Functor f => Recursive (F f a) Source # 

Methods

project :: F f a -> Base (F f a) (F f a) Source #

cata :: (Base (F f a) a -> a) -> F f a -> a Source #

para :: (Base (F f a) (F f a, a) -> a) -> F f a -> a Source #

gpara :: (Corecursive (F f a), Comonad w) => (forall b. Base (F f a) (w b) -> w (Base (F f a) b)) -> (Base (F f a) (EnvT (F f a) w a) -> a) -> F f a -> a Source #

prepro :: Corecursive (F f a) => (forall b. Base (F f a) b -> Base (F f a) b) -> (Base (F f a) a -> a) -> F f a -> a Source #

gprepro :: (Corecursive (F f a), Comonad w) => (forall b. Base (F f a) (w b) -> w (Base (F f a) b)) -> (forall c. Base (F f a) c -> Base (F f a) c) -> (Base (F f a) (w a) -> a) -> F f a -> a Source #

Functor f => Recursive (Free f a) Source # 

Methods

project :: Free f a -> Base (Free f a) (Free f a) Source #

cata :: (Base (Free f a) a -> a) -> Free f a -> a Source #

para :: (Base (Free f a) (Free f a, a) -> a) -> Free f a -> a Source #

gpara :: (Corecursive (Free f a), Comonad w) => (forall b. Base (Free f a) (w b) -> w (Base (Free f a) b)) -> (Base (Free f a) (EnvT (Free f a) w a) -> a) -> Free f a -> a Source #

prepro :: Corecursive (Free f a) => (forall b. Base (Free f a) b -> Base (Free f a) b) -> (Base (Free f a) a -> a) -> Free f a -> a Source #

gprepro :: (Corecursive (Free f a), Comonad w) => (forall b. Base (Free f a) (w b) -> w (Base (Free f a) b)) -> (forall c. Base (Free f a) c -> Base (Free f a) c) -> (Base (Free f a) (w a) -> a) -> Free f a -> a Source #

(Functor m, Functor f) => Recursive (FreeT f m a) Source # 

Methods

project :: FreeT f m a -> Base (FreeT f m a) (FreeT f m a) Source #

cata :: (Base (FreeT f m a) a -> a) -> FreeT f m a -> a Source #

para :: (Base (FreeT f m a) (FreeT f m a, a) -> a) -> FreeT f m a -> a Source #

gpara :: (Corecursive (FreeT f m a), Comonad w) => (forall b. Base (FreeT f m a) (w b) -> w (Base (FreeT f m a) b)) -> (Base (FreeT f m a) (EnvT (FreeT f m a) w a) -> a) -> FreeT f m a -> a Source #

prepro :: Corecursive (FreeT f m a) => (forall b. Base (FreeT f m a) b -> Base (FreeT f m a) b) -> (Base (FreeT f m a) a -> a) -> FreeT f m a -> a Source #

gprepro :: (Corecursive (FreeT f m a), Comonad w) => (forall b. Base (FreeT f m a) (w b) -> w (Base (FreeT f m a) b)) -> (forall c. Base (FreeT f m a) c -> Base (FreeT f m a) c) -> (Base (FreeT f m a) (w a) -> a) -> FreeT f m a -> a Source #

(Functor w, Functor f) => Recursive (CofreeT f w a) Source # 

Methods

project :: CofreeT f w a -> Base (CofreeT f w a) (CofreeT f w a) Source #

cata :: (Base (CofreeT f w a) a -> a) -> CofreeT f w a -> a Source #

para :: (Base (CofreeT f w a) (CofreeT f w a, a) -> a) -> CofreeT f w a -> a Source #

gpara :: (Corecursive (CofreeT f w a), Comonad w) => (forall b. Base (CofreeT f w a) (w b) -> w (Base (CofreeT f w a) b)) -> (Base (CofreeT f w a) (EnvT (CofreeT f w a) w a) -> a) -> CofreeT f w a -> a Source #

prepro :: Corecursive (CofreeT f w a) => (forall b. Base (CofreeT f w a) b -> Base (CofreeT f w a) b) -> (Base (CofreeT f w a) a -> a) -> CofreeT f w a -> a Source #

gprepro :: (Corecursive (CofreeT f w a), Comonad w) => (forall b. Base (CofreeT f w a) (w b) -> w (Base (CofreeT f w a) b)) -> (forall c. Base (CofreeT f w a) c -> Base (CofreeT f w a) c) -> (Base (CofreeT f w a) (w a) -> a) -> CofreeT f w a -> a Source #

Combinators

gcata Source #

Arguments

:: (Recursive t, Comonad w) 
=> (forall b. Base t (w b) -> w (Base t b))

a distributive law

-> (Base t (w a) -> a)

a (Base t)-w-algebra

-> t

fixed point

-> a 

A generalized catamorphism. With the appropriate distributive law, it can be specialized to any fold: a cata, a para, a zygo, etc.

For example, we could build a version of zygo in which the sub-trees are folded with a para instead of a cata:

-- |
-- >>> splitInThree "one, two, three, four"
-- Just ("one","two","three, four")
splitInThree :: String -> Maybe (String,String,String)
splitInThree = gcata (dist splitAtCommaSpaceF) splitInThreeF

splitInThreeF :: ListF Char ( (String, Maybe (String,String))
                            , Maybe (String,String,String)
                            )
              -> Maybe (String,String,String)
splitInThreeF (Cons ',' ((_, Just (' ':ys,zs)), _)) = Just ([], ys, zs)
splitInThreeF (Cons x (_, Just (xs,ys,zs)))         = Just (x:xs, ys, zs)
splitInThreeF _                                     = Nothing

dist :: Corecursive t
     => (Base t (t,b) -> b)
     -> Base t ((t,b), a)
     -> ((t,b), Base t a)
dist f baseTBA = let baseTB = fst <$> baseTBA
                     baseT  = fst <$> baseTB
                     baseA  = snd <$> baseTBA
                     b      = f baseTB
                     t      = embed baseT
                 in ((t,b), baseA)

zygo :: Recursive t => (Base t b -> b) -> (Base t (b, a) -> a) -> t -> a Source #

A variant of para in which instead of also giving the original sub-tree, the recursive positions give the result of applying a cata to that sub-tree. Thanks to the shared structure, zygo is more efficient than manually applying cata inside a para.

-- | A variant of 'nub' which keeps the last occurrence instead of the first.
--
-- >>> nub [1,2,2,3,2,1,1,4]
-- [1,2,3,4]
-- >>> nubEnd [1,2,2,3,2,1,1,4]
-- [3,2,1,4]
nubEnd :: [Int] -> [Int]
nubEnd = zygo gather go where
  gather :: ListF Int (Set Int) -> Set Int
  gather Nil         = Set.empty
  gather (Cons x xs) = Set.insert x xs

  go :: ListF Int (Set Int, [Int]) -> [Int]
  go Nil                = []
  go (Cons x (seen,xs)) = if Set.member x seen then xs else x:xs

gzygo :: (Recursive t, Comonad w) => (Base t b -> b) -> (forall c. Base t (w c) -> w (Base t c)) -> (Base t (EnvT b w a) -> a) -> t -> a Source #

A generalized zygomorphism. Like zygo, each recursive position gives the result of the fold on its sub-tree and also the result of a cata on that sub-tree. Depending on the distributive law, more information about that sub-tree may also be provided.

For example, we could build a "zygomorphic zygomorphism", in which the result of a second cata is also provided:

-- | Is any path from a node to a leaf more than twice as long as the path from
-- that node to another leaf?
--
-- >>> take 10 $ map isUnbalanced $ iterate (\t -> leaf () `branch` t) $ leaf ()
-- [False,False,False,False,True,True,True,True,True,True]
isUnbalanced :: Tree a -> Bool
isUnbalanced = gzygo minDepthF (distZygo maxDepthF) isUnbalancedF

minDepthF :: TreeF a Int -> Int
minDepthF (Leaf _)     = 1
minDepthF (Branch x y) = 1 + min x y

maxDepthF :: TreeF a Int -> Int
maxDepthF (Leaf _)     = 1
maxDepthF (Branch x y) = 1 + max x y

isUnbalancedF :: TreeF a (EnvT Int ((,) Int) Bool) -> Bool
isUnbalancedF (Leaf _) = False
isUnbalancedF (Branch (EnvT min1 (max1, unbalanced1))
                      (EnvT min2 (max2, unbalanced2)))
  = unbalanced1 || unbalanced2 || 2 * (1 + min min1 min2) < (1 + max max1 max2)

histo :: Recursive t => (Base t (Cofree (Base t) a) -> a) -> t -> a Source #

Course-of-value iteration. Similar to para in that each recursive position also includes a representation of its original sub-tree in addition to the result of folding that sub-tree, except that representation also includes the results of folding the sub-trees of that sub-tree, as well as the results of their sub-trees, etc.

Useful for folding more than one layer at a time:

-- |
-- >>> pairs [1,2,3,4]
-- Just [(1,2),(3,4)]
-- >>> pairs [1,2,3,4,5]
-- Nothing
pairs :: [Int] -> Maybe [(Int,Int)]
pairs = histo pairsF

pairsF :: ListF Int (Cofree (ListF Int) (Maybe [(Int,Int)]))
       -> Maybe [(Int,Int)]
pairsF Nil                                    = Just []
pairsF (Cons x (_ :< Cons y (Just xys :< _))) = Just ((x,y):xys)
pairsF _                                      = Nothing

ghisto :: (Recursive t, Comonad w) => (forall b. Base t (w b) -> w (Base t b)) -> (Base t (CofreeT (Base t) w a) -> a) -> t -> a Source #

Distributive laws

distCata :: Functor f => f (Identity a) -> Identity (f a) Source #

distPara :: Corecursive t => Base t (t, a) -> (t, Base t a) Source #

distParaT :: (Corecursive t, Comonad w) => (forall b. Base t (w b) -> w (Base t b)) -> Base t (EnvT t w a) -> EnvT t w (Base t a) Source #

distZygo Source #

Arguments

:: Functor f 
=> (f b -> b) 
-> f (b, a) -> (b, f a)

A distributive for semi-mutual recursion

distZygoT :: (Functor f, Comonad w) => (f b -> b) -> (forall c. f (w c) -> w (f c)) -> f (EnvT b w a) -> EnvT b w (f a) Source #

distHisto :: Functor f => f (Cofree f a) -> Cofree f (f a) Source #

distGHisto :: (Functor f, Functor h) => (forall b. f (h b) -> h (f b)) -> f (CofreeT f h a) -> CofreeT f h (f a) Source #

Unfolding

class Functor (Base t) => Corecursive t where Source #

A recursive datatype which can be rolled up one recursion layer at a time.

For example, a value of type ListF a [a] can be rolled up into a [a]. This [a] can then be used in a Cons to construct another List F a [a], which can be rolled up as well, and so on.

Typically, Corecursive types also have a Recursive instance, in which case embed and project are inverses.

Minimal complete definition

embed

Methods

embed :: Base t t -> t Source #

Roll up a single recursion layer.

>>> embed (Cons 1 [2,3])
[1,2,3]

ana Source #

Arguments

:: (a -> Base t a)

a (Base t)-coalgebra

-> a

seed

-> t

resulting fixed point

A generalization of unfoldr. The starting seed is expanded into a base functor whose recursive positions contain more seeds, which are themselves expanded, and so on.

-- |
-- >>> enumFromTo 1 4
-- [1,2,3,4]
enumFromTo :: Int -> Int -> [Int]
enumFromTo lo hi = ana go lo where
  go :: Int -> ListF Int Int
  go i = if i > hi then Nil else Cons i (i+1)

apo :: (a -> Base t (Either t a)) -> a -> t Source #

A variant of ana in which recursive positions may contain a sub-tree instead of a seed.

Useful for short-circuiting the remainder of the unfolding:

-- |
-- >>> mergeSortedLists [1,4,6,9] [2,4,6,7,10]
-- [1,2,4,4,6,6,7,9,10]
mergeSortedLists :: [Int] -> [Int] -> [Int]
mergeSortedLists xs1 xs2 = apo mergeSortedListsF (xs1,xs2)

mergeSortedListsF :: ([Int],[Int]) -> ListF Int (Either [Int] ([Int],[Int]))
mergeSortedListsF ([], []) = Nil
mergeSortedListsF ([], x:xs2) = Cons x $ Left xs2
mergeSortedListsF (x:xs1, []) = Cons x $ Left xs1
mergeSortedListsF (x1:xs1, x2:xs2)
  | x1 <= x2  = Cons x1 $ Right (xs1, x2:xs2)
  | otherwise = Cons x2 $ Right (x1:xs1, xs2)

postpro :: Recursive t => (forall b. Base t b -> Base t b) -> (a -> Base t a) -> a -> t Source #

Fokkinga's postpromorphism. Uses an ana on the seed, and then applies the natural transformation n times to the base functors produced at depth n. The outermost base functor has depth zero.

-- |
-- >>> take 8 $ iterate (*2) 1
-- [1,2,4,8,16,32,64,128]
iterate :: (Int -> Int) -> Int -> [Int]
iterate f = postpro apply go where
  apply :: ListF Int b -> ListF Int b
  apply Nil        = Nil
  apply (Cons x y) = Cons (f x) y

  go :: Int -> ListF Int Int
  go x = Cons x x

gpostpro :: (Recursive t, Monad m) => (forall b. m (Base t b) -> Base t (m b)) -> (forall c. Base t c -> Base t c) -> (a -> Base t (m a)) -> a -> t Source #

A generalized postpromorphism. The seed is expanded using the operation corresponding to the given distributive law, and then like in postpro, the natural transformation is applied n times to the base functors at depth n.

For example, we could expand the seed using a gapo:

-- |
-- >>> upThenFork 4
-- [(1,1),(2,2),(3,3),(4,4),(5,3),(6,2),(7,1)]
upThenFork :: Int -> [(Int,Int)]
upThenFork n = gpostpro (distGApo down) incrementFst up 1 where
  incrementFst :: ListF (Int,b) c -> ListF (Int,b) c
  incrementFst Nil             = Nil
  incrementFst (Cons (x, y) z) = Cons (1+x, y) z

  up :: Int -> ListF (Int,Int) (Either Int Int)
  up i = Cons (1,i) (if i == n then Left (n-1) else Right (i+1))

  down :: Int -> ListF (Int,Int) Int
  down 0 = Nil
  down i = Cons (1,i) (i-1)

Instances

Corecursive Natural Source # 

Methods

embed :: Base Natural Natural -> Natural Source #

ana :: (a -> Base Natural a) -> a -> Natural Source #

apo :: (a -> Base Natural (Either Natural a)) -> a -> Natural Source #

postpro :: Recursive Natural => (forall b. Base Natural b -> Base Natural b) -> (a -> Base Natural a) -> a -> Natural Source #

gpostpro :: (Recursive Natural, Monad m) => (forall b. m (Base Natural b) -> Base Natural (m b)) -> (forall c. Base Natural c -> Base Natural c) -> (a -> Base Natural (m a)) -> a -> Natural Source #

Corecursive [a] Source # 

Methods

embed :: Base [a] [a] -> [a] Source #

ana :: (a -> Base [a] a) -> a -> [a] Source #

apo :: (a -> Base [a] (Either [a] a)) -> a -> [a] Source #

postpro :: Recursive [a] => (forall b. Base [a] b -> Base [a] b) -> (a -> Base [a] a) -> a -> [a] Source #

gpostpro :: (Recursive [a], Monad m) => (forall b. m (Base [a] b) -> Base [a] (m b)) -> (forall c. Base [a] c -> Base [a] c) -> (a -> Base [a] (m a)) -> a -> [a] Source #

Corecursive (Maybe a) Source # 

Methods

embed :: Base (Maybe a) (Maybe a) -> Maybe a Source #

ana :: (a -> Base (Maybe a) a) -> a -> Maybe a Source #

apo :: (a -> Base (Maybe a) (Either (Maybe a) a)) -> a -> Maybe a Source #

postpro :: Recursive (Maybe a) => (forall b. Base (Maybe a) b -> Base (Maybe a) b) -> (a -> Base (Maybe a) a) -> a -> Maybe a Source #

gpostpro :: (Recursive (Maybe a), Monad m) => (forall b. m (Base (Maybe a) b) -> Base (Maybe a) (m b)) -> (forall c. Base (Maybe a) c -> Base (Maybe a) c) -> (a -> Base (Maybe a) (m a)) -> a -> Maybe a Source #

Corecursive (NonEmpty a) Source # 

Methods

embed :: Base (NonEmpty a) (NonEmpty a) -> NonEmpty a Source #

ana :: (a -> Base (NonEmpty a) a) -> a -> NonEmpty a Source #

apo :: (a -> Base (NonEmpty a) (Either (NonEmpty a) a)) -> a -> NonEmpty a Source #

postpro :: Recursive (NonEmpty a) => (forall b. Base (NonEmpty a) b -> Base (NonEmpty a) b) -> (a -> Base (NonEmpty a) a) -> a -> NonEmpty a Source #

gpostpro :: (Recursive (NonEmpty a), Monad m) => (forall b. m (Base (NonEmpty a) b) -> Base (NonEmpty a) (m b)) -> (forall c. Base (NonEmpty a) c -> Base (NonEmpty a) c) -> (a -> Base (NonEmpty a) (m a)) -> a -> NonEmpty a Source #

Functor f => Corecursive (Nu f) Source # 

Methods

embed :: Base (Nu f) (Nu f) -> Nu f Source #

ana :: (a -> Base (Nu f) a) -> a -> Nu f Source #

apo :: (a -> Base (Nu f) (Either (Nu f) a)) -> a -> Nu f Source #

postpro :: Recursive (Nu f) => (forall b. Base (Nu f) b -> Base (Nu f) b) -> (a -> Base (Nu f) a) -> a -> Nu f Source #

gpostpro :: (Recursive (Nu f), Monad m) => (forall b. m (Base (Nu f) b) -> Base (Nu f) (m b)) -> (forall c. Base (Nu f) c -> Base (Nu f) c) -> (a -> Base (Nu f) (m a)) -> a -> Nu f Source #

Functor f => Corecursive (Mu f) Source # 

Methods

embed :: Base (Mu f) (Mu f) -> Mu f Source #

ana :: (a -> Base (Mu f) a) -> a -> Mu f Source #

apo :: (a -> Base (Mu f) (Either (Mu f) a)) -> a -> Mu f Source #

postpro :: Recursive (Mu f) => (forall b. Base (Mu f) b -> Base (Mu f) b) -> (a -> Base (Mu f) a) -> a -> Mu f Source #

gpostpro :: (Recursive (Mu f), Monad m) => (forall b. m (Base (Mu f) b) -> Base (Mu f) (m b)) -> (forall c. Base (Mu f) c -> Base (Mu f) c) -> (a -> Base (Mu f) (m a)) -> a -> Mu f Source #

Functor f => Corecursive (Fix f) Source # 

Methods

embed :: Base (Fix f) (Fix f) -> Fix f Source #

ana :: (a -> Base (Fix f) a) -> a -> Fix f Source #

apo :: (a -> Base (Fix f) (Either (Fix f) a)) -> a -> Fix f Source #

postpro :: Recursive (Fix f) => (forall b. Base (Fix f) b -> Base (Fix f) b) -> (a -> Base (Fix f) a) -> a -> Fix f Source #

gpostpro :: (Recursive (Fix f), Monad m) => (forall b. m (Base (Fix f) b) -> Base (Fix f) (m b)) -> (forall c. Base (Fix f) c -> Base (Fix f) c) -> (a -> Base (Fix f) (m a)) -> a -> Fix f Source #

Corecursive (Either a b) Source # 

Methods

embed :: Base (Either a b) (Either a b) -> Either a b Source #

ana :: (a -> Base (Either a b) a) -> a -> Either a b Source #

apo :: (a -> Base (Either a b) (Either (Either a b) a)) -> a -> Either a b Source #

postpro :: Recursive (Either a b) => (forall c. Base (Either a b) c -> Base (Either a b) c) -> (a -> Base (Either a b) a) -> a -> Either a b Source #

gpostpro :: (Recursive (Either a b), Monad m) => (forall c. m (Base (Either a b) c) -> Base (Either a b) (m c)) -> (forall c. Base (Either a b) c -> Base (Either a b) c) -> (a -> Base (Either a b) (m a)) -> a -> Either a b Source #

Functor f => Corecursive (Cofree f a) Source # 

Methods

embed :: Base (Cofree f a) (Cofree f a) -> Cofree f a Source #

ana :: (a -> Base (Cofree f a) a) -> a -> Cofree f a Source #

apo :: (a -> Base (Cofree f a) (Either (Cofree f a) a)) -> a -> Cofree f a Source #

postpro :: Recursive (Cofree f a) => (forall b. Base (Cofree f a) b -> Base (Cofree f a) b) -> (a -> Base (Cofree f a) a) -> a -> Cofree f a Source #

gpostpro :: (Recursive (Cofree f a), Monad m) => (forall b. m (Base (Cofree f a) b) -> Base (Cofree f a) (m b)) -> (forall c. Base (Cofree f a) c -> Base (Cofree f a) c) -> (a -> Base (Cofree f a) (m a)) -> a -> Cofree f a Source #

Functor f => Corecursive (F f a) Source # 

Methods

embed :: Base (F f a) (F f a) -> F f a Source #

ana :: (a -> Base (F f a) a) -> a -> F f a Source #

apo :: (a -> Base (F f a) (Either (F f a) a)) -> a -> F f a Source #

postpro :: Recursive (F f a) => (forall b. Base (F f a) b -> Base (F f a) b) -> (a -> Base (F f a) a) -> a -> F f a Source #

gpostpro :: (Recursive (F f a), Monad m) => (forall b. m (Base (F f a) b) -> Base (F f a) (m b)) -> (forall c. Base (F f a) c -> Base (F f a) c) -> (a -> Base (F f a) (m a)) -> a -> F f a Source #

Functor f => Corecursive (Free f a) Source #

It may be better to work with the instance for F directly.

Methods

embed :: Base (Free f a) (Free f a) -> Free f a Source #

ana :: (a -> Base (Free f a) a) -> a -> Free f a Source #

apo :: (a -> Base (Free f a) (Either (Free f a) a)) -> a -> Free f a Source #

postpro :: Recursive (Free f a) => (forall b. Base (Free f a) b -> Base (Free f a) b) -> (a -> Base (Free f a) a) -> a -> Free f a Source #

gpostpro :: (Recursive (Free f a), Monad m) => (forall b. m (Base (Free f a) b) -> Base (Free f a) (m b)) -> (forall c. Base (Free f a) c -> Base (Free f a) c) -> (a -> Base (Free f a) (m a)) -> a -> Free f a Source #

(Functor m, Functor f) => Corecursive (FreeT f m a) Source # 

Methods

embed :: Base (FreeT f m a) (FreeT f m a) -> FreeT f m a Source #

ana :: (a -> Base (FreeT f m a) a) -> a -> FreeT f m a Source #

apo :: (a -> Base (FreeT f m a) (Either (FreeT f m a) a)) -> a -> FreeT f m a Source #

postpro :: Recursive (FreeT f m a) => (forall b. Base (FreeT f m a) b -> Base (FreeT f m a) b) -> (a -> Base (FreeT f m a) a) -> a -> FreeT f m a Source #

gpostpro :: (Recursive (FreeT f m a), Monad m) => (forall b. m (Base (FreeT f m a) b) -> Base (FreeT f m a) (m b)) -> (forall c. Base (FreeT f m a) c -> Base (FreeT f m a) c) -> (a -> Base (FreeT f m a) (m a)) -> a -> FreeT f m a Source #

(Functor w, Functor f) => Corecursive (CofreeT f w a) Source # 

Methods

embed :: Base (CofreeT f w a) (CofreeT f w a) -> CofreeT f w a Source #

ana :: (a -> Base (CofreeT f w a) a) -> a -> CofreeT f w a Source #

apo :: (a -> Base (CofreeT f w a) (Either (CofreeT f w a) a)) -> a -> CofreeT f w a Source #

postpro :: Recursive (CofreeT f w a) => (forall b. Base (CofreeT f w a) b -> Base (CofreeT f w a) b) -> (a -> Base (CofreeT f w a) a) -> a -> CofreeT f w a Source #

gpostpro :: (Recursive (CofreeT f w a), Monad m) => (forall b. m (Base (CofreeT f w a) b) -> Base (CofreeT f w a) (m b)) -> (forall c. Base (CofreeT f w a) c -> Base (CofreeT f w a) c) -> (a -> Base (CofreeT f w a) (m a)) -> a -> CofreeT f w a Source #

Combinators

gapo :: Corecursive t => (b -> Base t b) -> (a -> Base t (Either b a)) -> a -> t Source #

A variant of apo in which the short-circuiting sub-tree is described using an ana.

-- |
-- >>> upThenDown 4
-- [1,2,3,4,3,2,1]
upThenDown :: Int -> [Int]
upThenDown n = gapo down up 1 where
  up :: Int -> ListF Int (Either Int Int)
  up i = Cons i (if i == n then Left (n-1) else Right (i+1))

  down :: Int -> ListF Int Int
  down 0 = Nil
  down i = Cons i (i-1)

gana Source #

Arguments

:: (Corecursive t, Monad m) 
=> (forall b. m (Base t b) -> Base t (m b))

a distributive law

-> (a -> Base t (m a))

a (Base t)-m-coalgebra

-> a

seed

-> t 

A generalized anamorphism. With the appropriate distributive law, it can be specialized to any unfold: an ana, an apo, a futu, etc.

For example, we could build a version of gapo with three phases instead of two:

-- |
-- >>> upDownUp 4
-- [1,2,3,4,3,2,1,2,3,4]
upDownUp :: Int -> [Int]
upDownUp n = gana (dist upAgain down) up 1 where
  up :: Int -> ListF Int (Int `Either` Int `Either` Int)
  up i = Cons i (if i == n then Left (Right (n-1)) else Right (i+1))

  down :: Int -> ListF Int (Either Int Int)
  down i = Cons i (if i == 1 then Left 2 else Right (i-1))

  upAgain :: Int -> ListF Int Int
  upAgain i = if i > n then Nil else Cons i (i+1)

dist :: Functor f
     => (c -> f c)
     -> (b -> f (Either c b))
     -> c `Either` b `Either` f a
     -> f (c `Either` b `Either` a)
dist f _ (Left (Left z))  = Left <$> Left <$> f z
dist _ g (Left (Right y)) = Left <$> g y
dist _ _ (Right fx)       = Right <$> fx

futu :: Corecursive t => (a -> Base t (Free (Base t) a)) -> a -> t Source #

A variant of ana in which more than one recursive layer can be generated before returning the next seed.

Useful for inserting a group of elements all at once:

-- |
-- >>> spaceOutCommas "foo,bar,baz"
-- "foo, bar, baz"
spaceOutCommas :: String -> String
spaceOutCommas = futu go where
  go :: String -> ListF Char (Free (ListF Char) String)
  go []       = Nil
  go (',':xs) = Cons ',' (Free (Cons ' ' (Pure xs)))
  go (x:xs)   = Cons x (Pure xs)

gfutu :: (Corecursive t, Monad m) => (forall b. m (Base t b) -> Base t (m b)) -> (a -> Base t (FreeT (Base t) m a)) -> a -> t Source #

Distributive laws

distAna :: Functor f => Identity (f a) -> f (Identity a) Source #

distApo :: Recursive t => Either t (Base t a) -> Base t (Either t a) Source #

distGApo :: Functor f => (b -> f b) -> Either b (f a) -> f (Either b a) Source #

distGApoT :: (Functor f, Functor m) => (b -> f b) -> (forall c. m (f c) -> f (m c)) -> ExceptT b m (f a) -> f (ExceptT b m a) Source #

distFutu :: Functor f => Free f (f a) -> f (Free f a) Source #

distGFutu :: (Functor f, Functor h) => (forall b. h (f b) -> f (h b)) -> FreeT f h (f a) -> f (FreeT f h a) Source #

Refolding

hylo :: Functor f => (f b -> b) -> (a -> f a) -> a -> b Source #

An optimized version of cata f . ana g.

Useful when your recursion structure is shaped like a particular recursive datatype, but you're neither consuming nor producing that recursive datatype. For example, the recursion structure of merge sort is a binary tree, but its input and output is a list, not a binary tree.

-- |
-- >>> sort [1,5,2,8,4,9,8]
-- [1,2,4,5,8,8,9]
sort :: [Int] -> [Int]
sort = hylo merge split where
  split :: [Int] -> TreeF Int [Int]
  split [x] = Leaf x
  split xs  = uncurry Branch $ splitAt (length xs `div` 2) xs

  merge :: TreeF Int [Int] -> [Int]
  merge (Leaf x)         = [x]
  merge (Branch xs1 xs2) = mergeSortedLists xs1 xs2

ghylo :: (Comonad w, Functor f, Monad m) => (forall c. f (w c) -> w (f c)) -> (forall d. m (f d) -> f (m d)) -> (f (w b) -> b) -> (a -> f (m a)) -> a -> b Source #

A generalized hylomorphism. Like a hylo, this is an optimized version of an unfold followed by a fold. The fold and unfold operations correspond to the given distributive laws.

For example, one way to implement fib n is to compute fib 1 up to fib n. This is a simple linear recursive structure which we can model by unfolding our seed n into a Fix Maybe containing n Justs. To do that, a cata is sufficient. We then fold the sub-tree containing i Justs by computing fib i out of fib (i-1) and fib (i-2), the results of folding two smaller sub-trees. To see more than one such result, we need a histo.

-- |
-- >>> fmap fib [0..8]
-- [1,1,2,3,5,8,13,21,34]
fib :: Int -> Integer
fib = ghylo distHisto distAna addF down where
  down :: Int -> Maybe (Identity Int)
  down 0 = Nothing
  down n = Just (Identity (n-1))

  addF :: Maybe (Cofree Maybe Integer) -> Integer
  addF Nothing                     = 1
  addF (Just (_ :< Nothing))       = 1
  addF (Just (x :< Just (y :< _))) = x + y

chrono :: Functor f => (f (Cofree f b) -> b) -> (a -> f (Free f a)) -> a -> b Source #

An optimized version of a futu followed by a histo.

-- |
-- >>> putStr $ decompressImage [(1,'.'),(1,'*'),(3,'.'),(4,'*')]
-- .*.
-- ..*
-- ***
decompressImage :: [(Int,Char)] -> String
decompressImage = chrono linesOf3 decodeRLE where
  decodeRLE :: [(Int,Char)] -> ListF Char (Free (ListF Char) [(Int,Char)])
  decodeRLE []          = Nil
  decodeRLE ((n,c):ncs) = Cons c $ do
    replicateM_ (n-1) $ Free $ Cons c $ Pure ()
    pure ncs

  linesOf3 :: ListF Char (Cofree (ListF Char) String) -> String
  linesOf3 (Cons c1 (_ :< Cons c2 (_ :< Cons c3 (cs :< _)))) = c1:c2:c3:'\n':cs
  linesOf3 _                                                 = ""

gchrono :: (Functor f, Comonad w, Monad m) => (forall c. f (w c) -> w (f c)) -> (forall c. m (f c) -> f (m c)) -> (f (CofreeT f w b) -> b) -> (a -> f (FreeT f m a)) -> a -> b Source #

An optimized version of a gfutu followed by a ghisto.

-- |
-- >>> putStr $ decompressImage [1,1,3,4]
-- .*.
-- ..*
-- ***
decompressImage :: [Int] -> String
decompressImage = gchrono (distZygo toggle) distAna linesOf3 decodeRLE where
  decodeRLE :: [Int] -> ListF Bool (Free (ListF Bool) [Int])
  decodeRLE [] = Nil
  decodeRLE (1:ns) = Cons True $ pure ns
  decodeRLE (n:ns) = Cons False $ do
    replicateM_ (n-2) $ writeBool False
    writeBool True
    pure ns

  toggle :: ListF Bool Char -> Char
  toggle Nil              = '.'
  toggle (Cons False c)   = c
  toggle (Cons True '.')  = '*'
  toggle (Cons True _)    = '.'

  linesOf3 :: ListF Bool (CofreeT (ListF Bool) ((,) Char) String) -> String
  linesOf3 (Cons b1 (CofreeT (c2, _ :< Cons _
                    (CofreeT (c3, _ :< Cons _
                    (CofreeT (_,  s :< _)))))))
    = toggle (Cons b1 c2) : c2 : c3 : '\n' : s
  linesOf3 _ = ""

  writeBool :: Bool -> Free (ListF Bool) ()
  writeBool b = FreeT $ Identity $ Free $ Cons b $ pure ()

Changing representation

refix :: (Recursive s, Corecursive t, Base s ~ Base t) => s -> t Source #

>>> refix ["foo", "bar"] :: Fix (ListF String)
Fix (Cons "foo" (Fix (Cons "bar" (Fix Nil))))

Common names

fold :: Recursive t => (Base t a -> a) -> t -> a Source #

A friendlier name for cata.

gfold Source #

Arguments

:: (Recursive t, Comonad w) 
=> (forall b. Base t (w b) -> w (Base t b))

a distributive law

-> (Base t (w a) -> a)

a (Base t)-w-algebra

-> t

fixed point

-> a 

A generalized catamorphism. With the appropriate distributive law, it can be specialized to any fold: a cata, a para, a zygo, etc.

For example, we could build a version of zygo in which the sub-trees are folded with a para instead of a cata:

-- |
-- >>> splitInThree "one, two, three, four"
-- Just ("one","two","three, four")
splitInThree :: String -> Maybe (String,String,String)
splitInThree = gcata (dist splitAtCommaSpaceF) splitInThreeF

splitInThreeF :: ListF Char ( (String, Maybe (String,String))
                            , Maybe (String,String,String)
                            )
              -> Maybe (String,String,String)
splitInThreeF (Cons ',' ((_, Just (' ':ys,zs)), _)) = Just ([], ys, zs)
splitInThreeF (Cons x (_, Just (xs,ys,zs)))         = Just (x:xs, ys, zs)
splitInThreeF _                                     = Nothing

dist :: Corecursive t
     => (Base t (t,b) -> b)
     -> Base t ((t,b), a)
     -> ((t,b), Base t a)
dist f baseTBA = let baseTB = fst <$> baseTBA
                     baseT  = fst <$> baseTB
                     baseA  = snd <$> baseTBA
                     b      = f baseTB
                     t      = embed baseT
                 in ((t,b), baseA)

unfold :: Corecursive t => (a -> Base t a) -> a -> t Source #

A friendlier name for ana.

gunfold Source #

Arguments

:: (Corecursive t, Monad m) 
=> (forall b. m (Base t b) -> Base t (m b))

a distributive law

-> (a -> Base t (m a))

a (Base t)-m-coalgebra

-> a

seed

-> t 

A generalized anamorphism. With the appropriate distributive law, it can be specialized to any unfold: an ana, an apo, a futu, etc.

For example, we could build a version of gapo with three phases instead of two:

-- |
-- >>> upDownUp 4
-- [1,2,3,4,3,2,1,2,3,4]
upDownUp :: Int -> [Int]
upDownUp n = gana (dist upAgain down) up 1 where
  up :: Int -> ListF Int (Int `Either` Int `Either` Int)
  up i = Cons i (if i == n then Left (Right (n-1)) else Right (i+1))

  down :: Int -> ListF Int (Either Int Int)
  down i = Cons i (if i == 1 then Left 2 else Right (i-1))

  upAgain :: Int -> ListF Int Int
  upAgain i = if i > n then Nil else Cons i (i+1)

dist :: Functor f
     => (c -> f c)
     -> (b -> f (Either c b))
     -> c `Either` b `Either` f a
     -> f (c `Either` b `Either` a)
dist f _ (Left (Left z))  = Left <$> Left <$> f z
dist _ g (Left (Right y)) = Left <$> g y
dist _ _ (Right fx)       = Right <$> fx

refold :: Functor f => (f b -> b) -> (a -> f a) -> a -> b Source #

A friendlier name for hylo.

grefold :: (Comonad w, Functor f, Monad m) => (forall c. f (w c) -> w (f c)) -> (forall d. m (f d) -> f (m d)) -> (f (w b) -> b) -> (a -> f (m a)) -> a -> b Source #

A generalized hylomorphism. Like a hylo, this is an optimized version of an unfold followed by a fold. The fold and unfold operations correspond to the given distributive laws.

For example, one way to implement fib n is to compute fib 1 up to fib n. This is a simple linear recursive structure which we can model by unfolding our seed n into a Fix Maybe containing n Justs. To do that, a cata is sufficient. We then fold the sub-tree containing i Justs by computing fib i out of fib (i-1) and fib (i-2), the results of folding two smaller sub-trees. To see more than one such result, we need a histo.

-- |
-- >>> fmap fib [0..8]
-- [1,1,2,3,5,8,13,21,34]
fib :: Int -> Integer
fib = ghylo distHisto distAna addF down where
  down :: Int -> Maybe (Identity Int)
  down 0 = Nothing
  down n = Just (Identity (n-1))

  addF :: Maybe (Cofree Maybe Integer) -> Integer
  addF Nothing                     = 1
  addF (Just (_ :< Nothing))       = 1
  addF (Just (x :< Just (y :< _))) = x + y

Mendler-style

mcata :: (forall y. (y -> c) -> f y -> c) -> Fix f -> c Source #

Mendler-style iteration, a restriction of general recursion in which the recursive calls can only be applied to the recursive position. Equivalent to a cata.

Contrast the following with the example for cata: instead of already having the sum for the tail of the list, we have an opaque version of the rest of the list, and a function which can compute its sum.

-- |
-- >>> sum $ refix [1,2,3]
-- 6
sum :: Fix (ListF Int) -> Int
sum = mcata $ \recur -> \case
  Nil       -> 0
  Cons x xs -> x + recur xs

mhisto :: (forall y. (y -> c) -> (y -> f y) -> f y -> c) -> Fix f -> c Source #

Mendler-style course-of-value iteration, a restriction of general recursion in which the recursive calls can only be applied to smaller terms. Equivalent to histo in terms of expressiveness, but note that overlapping sub-problems aren't automatically cached by the Cofree.

Contrast the following with the example for histo: instead of already having the solution for the tail of the tail of the list, we unroll the list in order to obtain an opaque version of the tail of the tail of the list, and then we recur on it.

-- |
-- >>> pairs $ refix [1,2,3,4]
-- Just [(1,2),(3,4)]
-- >>> pairs $ refix [1,2,3,4,5]
-- Nothing
pairs :: Fix (ListF Int) -> Maybe [(Int,Int)]
pairs = mhisto $ \recur unroll -> \case
  Nil       -> Just []
  Cons x xs -> case unroll xs of
    Nil       -> Nothing
    Cons y ys -> ((x,y) :) <$> recur ys

Elgot (co)algebras

elgot :: Functor f => (f a -> a) -> (b -> Either a (f b)) -> b -> a Source #

Elgot algebras, a variant of hylo in which the anamorphism side may decide to stop unfolding and to produce a solution instead. Useful when the base functor does not have a constructor for the base case.

For example, in the following implementation of fib n, we naively recur on n-1 and n-2 until we hit the base case, at which point we stop unfolding. With hylo, we would use Branch to recur and Leaf to stop unfolding, whereas with elgot we can use Pair, a variant of TreeF which does not have a Leaf-like constructor for the base case.

data Pair a = Pair a a
  deriving Functor

-- |
-- >>> fmap fib [0..8]
-- [1,1,2,3,5,8,13,21,34]
fib :: Int -> Integer
fib = elgot merge split where
  split :: Int -> Either Integer (Pair Int)
  split 0 = Left 1
  split 1 = Left 1
  split n = Right $ Pair (n-1) (n-2)

  merge :: Pair Integer -> Integer
  merge (Pair x y) = x + y

coelgot :: Functor f => ((a, f b) -> b) -> (a -> f a) -> a -> b Source #

Elgot coalgebras, a variant of hylo in which the catamorphism side also has access to the seed which produced the sub-tree at that recursive position. See http://comonad.com/reader/2008/elgot-coalgebras/

Contrast the following with the example for elgot: the base case is detected while folding instead of while unfolding.

-- |
-- >>> fmap fib [0..8]
-- [1,1,2,3,5,8,13,21,34]
fib :: Int -> Integer
fib = coelgot merge split where
  split :: Int -> Pair Int
  split n = Pair (n-1) (n-2)

  merge :: (Int, Pair Integer) -> Integer
  merge (0, _) = 1
  merge (1, _) = 1
  merge (_, Pair x y) = x + y

Zygohistomorphic prepromorphisms

zygoHistoPrepro :: (Corecursive t, Recursive t) => (Base t b -> b) -> (forall c. Base t c -> Base t c) -> (Base t (EnvT b (Cofree (Base t)) a) -> a) -> t -> a Source #

The infamous "zygohistomorphic prepromorphism". There is nothing special about this particular construction, it just happens to have a name which sounds like gobbledygook, making it a prime target for jokes such as http://www.haskell.org/haskellwiki/Zygohistomorphic_prepromorphisms.

Once you become familiar with the vocabulary of this library, the name no longer sounds alien and is instead a very concise description of what it does:

  • It is a prepromorphism, meaning that it takes a recursive data structure of type t, such as a list or a tree, and applies the forall c. Base t c -> Base t c transformation n times at depth n. The transformed results are then combined into a final solution of type a by applying the Base t (EnvT b (Cofree (Base t)) a) -> a function repeatedly, folding the recursive structure down to a single value. This function is called an "algebra", and the other bullet points explain the various part of its complicated type. See prepro.
  • It is zygomorphic, meaning that an auxiliary Base t b -> b function combines the transformed values into an auxiliary solution of type b. The EnvT b gives the algebra access to those auxiliary results. See zygo.
  • It is histomorphic, meaning that a Cofree (Base t) gives the algebra access to the solutions it previously computed for all the descendents of the tree-like data structure being folded, not just those for the immediate children. See histo.

Here is an example function which uses all of those features:

  • It uses indentation to illustrate nesting, that is, it applies an indentation function n times at depth n.
  • It uses an auxiliary TreeF String String -> String function to compute the header of a group, which we choose to be the prefix shared by all sub-trees.
  • It alternates between two bullet styles, by looking at the solutions computed two levels below.
-- |
-- >>> let tree = ((leaf "0.1.1.1" `branch` leaf "0.1.1.2") `branch` leaf "0.1.2") `branch` (leaf "0.2.1" `branch` leaf "0.2.2")
-- >>> putStrLn (alternateBullets tree)
-- * 0.
--   - 0.1.
--   * 0.1.1.
--       - 0.1.1.1
--       - 0.1.1.2
--     * 0.1.2
--   - 0.2.
--     * 0.2.1
--     * 0.2.2
--
alternateBullets :: Tree String -> String
alternateBullets = zygoHistoPrepro mergeHeaders indent starThenDash

starThenDash :: TreeF String (EnvT String (Cofree (TreeF String)) String) -> String
starThenDash (Leaf s) = addBullet '*' s
starThenDash (Branch (EnvT headerL cofreeL)
                     (EnvT headerR cofreeR))
  = addBullet '*' (mergeHeaders (Branch headerL headerR)) ++ "\n"
 ++ dashThenStar headerL cofreeL ++ "\n"
 ++ dashThenStar headerR cofreeR

dashThenStar :: String -> Cofree (TreeF String) String -> String
dashThenStar _ (_ :< Leaf s) = addBullet '-' s
dashThenStar header (_ :< Branch (sL :< _) (sR :< _))
  = addBullet '-' header ++ "\n"
 ++ sL ++ "\n"
 ++ sR

-- |
-- >>> addBullet '*' "   foo"
-- "   * foo"
addBullet :: Char -> String -> String
addBullet bullet line = takeWhile (== ' ') line
                     ++ (bullet:" ")
                     ++ dropWhile (== ' ') line

Notice that the indentation of "* 0.1.1." is off by one! This is because the comonad-based implementation of zygoHistoPrepro is subtly incorrect.