--
-- An F-algebra over a carrier sort x is set of functions (and constants)
-- whose types involve x. In Haskell we can simulate this by a data definition
-- for a functor (F x) and a function (F x) -> x
data Algebra f c = Algebra (f c -> c)
data F1 x = Zero | One | Plus x x
f :: F1 Int -> Int
f Zero = 0
f One = 1
f (Plus x y) = x+y
g :: F1 [Int] -> [Int]
g Zero = []
g One = [1]
g (Plus x y) = x ++ y
alg1 :: Algebra F1 Int
alg1 = Algebra f
alg2 :: Algebra F1 [Int]
alg2 = Algebra g
data ListF a x = Nil | Cons a x
h :: ListF b Int -> Int
h Nil = 0
h (Cons x xs) = 1 + xs
alg3 :: Algebra (ListF a) Int
alg3 = Algebra h
-- An initial Algebra is the set of terms we can obtain be iteratively applying
-- the functions to the constants and other function applications. This set
-- can be simulated in Haskell by the data definitions.
-- here the function is Init :: alg (Init alg) -> Init alg
-- Note how this fits the pattern f :: T x -> x
data Initial alg = Init (alg (Initial alg))
ex1 :: Initial F1
ex1 = Init(Plus (Init One) (Init Zero))
ex2 :: Initial (ListF Int)
ex2 = Init(Cons 2 (Init Nil))
initialAlg :: Algebra f (Initial f)
initialAlg = Algebra Init
-- We can write functions by a case analysis over the functions and
-- constants that generate the initial algebra
len :: Num a => Initial (ListF b) -> a
len (Init Nil) = 0
len (Init (Cons x xs)) = 1 + len xs
app :: Initial (ListF a) -> Initial (ListF a) -> Initial (ListF a)
app (Init Nil) ys = ys
app (Init (Cons x xs)) ys = Init(Cons x (app xs ys))
-- An F-algebra, f, is said to be initial to any other algebra, g, if there
-- is a UNIQUE homomorphism, from f to g (this is an arrow in the category of
-- F algebras). We can show the existence of this homomorphism by building
-- it as a datatype in Haskell. Note: that for each "f", (Arrow f a b) denotes
-- an arrow in the category of f-algebras.
data Arrow f a b = Arr (Algebra f a) (Algebra f b) (a->b)
-- For every Arrow (Arr (Algebra f) (Algebra g) h) it must be the case that
--
-- F a ---- fmap h -----> F b
-- | |
-- | f | g
-- | |
-- V V
-- a -------- h --------> b
valid :: (Eq b, Functor f) => Arrow f a b -> f a -> Bool
valid (Arr (Algebra f) (Algebra g) h) x = h(f x) == g(fmap h x)
-- To show the existence of "h" for any F-algebra means we can compute
-- a function with the type (a -> b) from the algebra. To do this we first define cata:
cata :: Functor f => (Algebra f b) -> Initial f -> b
cata (Algebra phi) (Init x) = phi(fmap (cata (Algebra phi)) x)
exhibit :: Functor f => Algebra f a -> Arrow f (Initial f) a
exhibit x = Arr initialAlg x (cata x)
-- Lots of functions can be written directly as cata's
len2 x = cata (Algebra phi) x
where phi Nil = 0
phi (Cons x n) = 1 + n
app2 x y = cata (Algebra phi) x
where phi Nil = y
phi (Cons x xs) = Init(Cons x xs)
-- With initiality comes the inductive proof method. So to prove something
-- (prop x) where x::Initial A
prop1 :: Initial (ListF Int) -> Bool
prop1 x = len(Init(Cons 1 x)) == 1 + len x
-- Prove: prop1 (Init Nil)
-- Assume prop1 xs
-- The prove: prop1 (Init (Cons x xs))
-- we need a function from A(prop x) -> prop x
data Proof = Simple Bool | Induct (Algebra (ListF Int) Proof)
p1 :: ListF a Proof -> Proof
p1 Nil = Simple(prop1 (Init Nil))
p1 (Cons x xs) = undefined
-- xs = Proof that prop1 ys holds
-- we need a proof that (prop1 (Init (Cons x ys))) holds
prop2 :: Initial (ListF a) -> Initial (ListF a) -> Bool
prop2 x y = len(app x y) == len x + len y
-------------------------------------------------------------------
-- CoAlgebras
data CoAlgebra f c = CoAlgebra (c -> f c)
unCoAlgebra (CoAlgebra x) = x
countdown :: CoAlgebra (ListF Int) Int
countdown = CoAlgebra f
where f 0 = Nil
f n = Cons n (n-1)
data StreamF n x = C n x
-----------------------------------------------------------
-- We can write CoAlgebras by expanding a "seed" into an F structure
-- filled with new seeds. seed -> F seed, the non-parameterized slots
-- can be filled with things computed from the seed. These are
-- sometimes called observations.
endsIn0s :: CoAlgebra (StreamF Integer) [Integer]
endsIn0s = CoAlgebra f
where f [] = C 0 []
f (x:xs) = C x xs
split :: CoAlgebra F1 Integer
split = CoAlgebra f
where f 0 = Zero
f 1 = One
f n = Plus (n-1) (n-2)
fibs :: CoAlgebra (StreamF Int) (Int,Int)
fibs = CoAlgebra f
where f (x,y) = C (x+y) (y,x+y)
instance Functor (StreamF n) where
fmap f (C x y) = C x (f y)
instance Functor F1 where
fmap f Zero = Zero
fmap f One = One
fmap f (Plus x y) = Plus (f x) (f y)
-----------------------------------------
-- Final CoAlgebras
-- sequences (branching trees?) of observations of the internal state
-- This allows us to iterate all the possible observations.
-- Sometimes these are infinite structures.
data Final f = Final (f (Final f))
unFinal :: Final a -> a (Final a)
unFinal (Final x) = x
finalCoalg :: CoAlgebra a (Final a)
finalCoalg = CoAlgebra unFinal
f1 :: Final (ListF a)
f1 = Final Nil
ones :: Final (StreamF Integer)
ones = Final(C 1 ones)
-----------------------------------------------------------
-- We can write functions producing elements in the sort of
-- Final CoAlgebras by expanding a "seed" into an F structure
-- filled with observations and recursive calls in the "slots"
-- then all thats really left is the observations.
nats :: Final (StreamF Integer)
nats = g 0 where g n = Final (C n (g (n+1)))
data NatF x = Z | S x
omega :: Final NatF
omega = f undefined
where f x = Final(S(f x))
n :: Int -> Final NatF
n x = f x
where f 0 = Final Z
f n = Final(S (f (n-1)))
----------------------------------------------------------------
-- A CoHomomorphism is an arrow in the category of F-CoAlgebras
data CoHom f a b = CoHom (CoAlgebra f a) (CoAlgebra f b) (a->b)
-- For every arrow in the category
-- (CoHom (CoAlgebra f) (CoAlgebra g) h) it must be the case that
--
-- F a ---- fmap h -----> F b
-- ^ ^
-- | |
-- | f | g
-- | |
-- a -------- h --------> b
covalid :: (Eq (f b), Functor f) => CoHom f a b -> a -> Bool
covalid (CoHom (CoAlgebra f) (CoAlgebra g) h) x = fmap h (f x) == g(h x)
-------------------------------------------------------------------------
-- A F-CoAlgebra, g, is Final if for any other F-CoAlgebra, f, there
-- is a unique F-CoAlgebra homomorphism, h, from f to g. We can show
-- its existence be building a function that computes it from the
-- CoAlgebra, f.
ana :: Functor f => (CoAlgebra f seed) -> seed -> (Final f)
ana (CoAlgebra phi) seed = Final(fmap (ana (CoAlgebra phi)) (phi seed))
exhibit2 :: Functor f => CoAlgebra f seed -> CoHom f seed (Final f)
exhibit2 x = CoHom x finalCoalg (ana x)
------------------------------------------------------------------
-- we use ana to iteratively unfold any coAgebra to record its observations
final1 = ana endsIn0s
final2 = ana split
final3 = ana fibs
tak :: Num a => a -> Final (StreamF b) -> [b]
tak 0 _ = []
tak n (Final (C x xs)) = x : tak (n-1) xs
fibs5 = tak 5 (final3 (1,1))
------------------------------------------------------
-- Lets use CoAlgebras to represent Points in the 2-D
-- plane as we would in an OO-language
data P x = P { xcoord :: Float
, ycoord :: Float
, move :: Float -> Float -> x}
pointF :: (Float,Float) -> P (Float,Float)
pointF (x,y) = P {xcoord = x, ycoord = y, move = \ m n -> (m+x,n+y) }
type Point = CoAlgebra P (Float,Float)
point1 :: Point
point1 = CoAlgebra pointF
--