Skip to content

Instantly share code, notes, and snippets.

@MiloXia
Created June 21, 2016 02:55
Show Gist options
  • Save MiloXia/bdca45ef16604bfe25db4b0b6f8ec12c to your computer and use it in GitHub Desktop.
Save MiloXia/bdca45ef16604bfe25db4b0b6f8ec12c to your computer and use it in GitHub Desktop.
F-algebra & F-coalgebra in Scala
import scala.language.higherKinds
import scalaz._, Scalaz._
object F_Algebra {
//### F-Algebra
//Fixed point of type constructor
case class Fix[F[_]](f: F[Fix[F]])
type Algebra[F[_], A] = Function[F[A], A] //a morphism from F(A) to A
//unFix :: Fix f -> f (Fix f)
def unFix[F[_]](fa: Fix[F]): F[Fix[F]] = fa match {
case Fix(a) => a
}
//cata :: Functor f => (f a -> a) -> Fix f -> a
def cata[F[_]: Functor, A](alg: F[A] => A): Fix[F] => A = {
val _unFix: Fix[F] => F[Fix[F]] = unFix[F] _
val fmap = Functor[F].map[Fix[F], A] _
val fmap_cata_alg: F[Fix[F]] => F[A] = fff => fmap(fff)(cata(alg))
alg compose fmap_cata_alg compose _unFix
}
//F-coalgebra
//ana :: Functor f => (a -> f a) -> a -> Fix f
def ana[F[_]: Functor, A](grow: A => F[A])(seed: A): Fix[F] = {
val fix: F[Fix[F]] => Fix[F] = Fix.apply[F] _
val fmap: F[A] => ((A) => Fix[F]) => F[Fix[F]] = Functor[F].map[A, Fix[F]] _
val fmap_ana_grow: F[A] => F[Fix[F]] = f => fmap(f)(ana(grow))
(fix compose fmap_ana_grow compose grow)(seed)
}
//test
def main(args: Array[String]) {
//foldr
trait ListF[+A, +B]
case object Nil extends ListF[Nothing, Nothing]
case class Cons[A, B](a: A, b: B) extends ListF[A, B]
//an endofunctor F
implicit def ListFFunctor[T] = new Functor[({type λ[α] = ListF[T, α]})#λ] {
def map[A, B](fa: ListF[T, A])(f: A => B): ListF[T, B] = fa match {
case Nil => Nil
case Cons(a, b) => Cons(a, f(b))
}
}
type ListFInt[A] = ListF[Int, A]
def nil = Fix[({type λ[α] = ListF[Nothing, Nothing]})#λ](Nil).asInstanceOf[Fix[ListFInt]]
def cons[B](a: Int, b: B) = Fix[({type λ[α] = ListF[Int, B]})#λ](Cons(a, b)).asInstanceOf[Fix[ListFInt]]
//algSum :: ListF Int Int -> Int
val algSum: ListFInt[Int] => Int = {
case Nil => 0
case Cons(e, acc) => e + acc
}
//lst :: Fix (ListF Int)
val lst: Fix[ListFInt] = cons(2, cons(3, cons(4, nil)))
val foldr = cata[ListFInt, Int](algSum)
println(foldr(lst)) //9
//unfold
val growListF: Int => ListFInt[Int] = {
case 0 => Nil
case x => Cons(x, x - 1)
}
val unfold = ana[ListFInt, Int](growListF) _
println(unfold(5)) //Fix(Cons(5,Fix(Cons(4,Fix(Cons(3,Fix(Cons(2,Fix(Cons(1,Fix(Nil)))))))))))
//Expr
trait ExprF[+A]
case class Const[A](i: Int) extends ExprF[A]
case class Add[A](left: A, right: A) extends ExprF[A]
case class Mul[A](left: A, right: A) extends ExprF[A]
implicit val ExprFFunctor = new Functor[ExprF] { //an endofunctor F
def map[A, B](fa: ExprF[A])(f: A => B): ExprF[B] = fa match {
case Const(i) => Const(i)
case Add(l, r) => Add(f(l), f(r))
case Mul(l, r) => Mul(f(l), f(r))
}
}
type Expr = Fix[ExprF]
def const(i: Int) = Fix[({type λ[α] = ExprF[Nothing]})#λ](Const(i)).asInstanceOf[Fix[ExprF]]
def mul[A](a: A, b: A) = Fix[({type λ[α] = ExprF[A]})#λ](Mul(a, b)).asInstanceOf[Fix[ExprF]]
def add[A](a: A, b: A) = Fix[({type λ[α] = ExprF[A]})#λ](Add(a, b)).asInstanceOf[Fix[ExprF]]
val testExpr1 = add(const(2), mul(const(3), const(4)))
val alg: Algebra[ExprF, Int] = {
case Const(i) => i
case Add(l, r) => l + r
case Mul(l, r) => l * r
}
def alg2: Algebra[ExprF, String] = {
case Const(i) => ('a'.toInt + i).toChar.toString
case Add(l, r) => l + r
case Mul(l, r) => (l zip r).map(t => s"${t._1}${t._2}").mkString
}
val eval = cata(alg)
println(eval(testExpr1)) //14
val eval2 = cata(alg2)
println(eval2(testExpr1)) //cde
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment