Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chronomorphisms in Scala

I'm learning recursion patterns, but I can't figure out the usefulness of futumorphism and histomorphism.

The couple of Scala examples I found look very unconvincing. I also found Haskell examples, but I can't figure them out.

Suppose we have functions like this:

def futu  [F[_]: Functor, X]: (X => Free[F, X]  ) => X => Fix[F]
def histo [F[_]: Functor, X]: (Cofree[F, X] => X) => Fix[F] => X

def ana   [F[_]: Functor, X]: (X => F[X]) => X => Fix[F]
def dynamo[F[_]: Functor, A, B]: (A => F[A], Cofree[F, B] => B) => A => B =  
  (coalg, alg) => ana(coalg) andThen histo(alg)

Can anyone give some good Scala examples of using

  • futu?
  • dynamo (histo within) applied to a typical dynamic programming problem?

And where exactly is the reference to "past" and "future" values ​​there?

like image 536
Sergey Sviridov Avatar asked Oct 20 '25 23:10

Sergey Sviridov


1 Answers

Recall futu (right, green is Bind, red is Pure) produces multiple steps at a time, and histo (left, Red is CoFree) consumes multiple steps at a time. Below you're pretty-printing a superscript exponential expression by looking into the history, or inserting a binary Plus tree when parsing an associative expression. enter image description here

Here are three examples of futu

// given the following function computes the adjacency list of an algebraic graph
def toAdj[V] = cata[[X] =>> Graph[V, X], Map[V, Set[V]]]{
  case Empty => Map()
  case Vertex(a) => Map(a -> Set())
  case Overlay(l, r) => l.mergeWith(r)(_ | _)
  case Connect(l, r) => l.mergeWith(r)(_ | _)
    .mergeWith(l.keySet.map((_, r.keySet)).toMap)(_ | _)
}
// this function creates an algebraic graph from an adjacency matrix
def fromAdj[K, V] = futu[[X] =>> Graph[V, X], Iterable[(V, Set[V])]]{
  case (v, nbs)::tail => Overlay(Free.Pure(tail), Free.Bind(
    Connect(Free.Bind(Vertex(v)),
            nbs.foldLeft(Free.Bind(Empty))((t, k) => Free.Bind(Overlay(t, Free.Bind(Vertex(k))))))))
  case Nil => Empty
}
// A simple algebraic graph constructor 
def path[V] = futu[[X] =>> Graph[V, X], Iterable[V]]{
  case x::y::Nil => Connect(Free.Bind(Vertex(x)), Free.Bind(Vertex(y)))
  case x::y::tail => Overlay(Free.Pure(x::y::Nil), Free.Pure(y::tail))
  case _ => Empty
}
// Converting a Tree instead
def tree[V] = futu[[X] =>> Graph[V, X], Tree[V]]{
  case Fix((v, Nil)) => Vertex(v)
  case Fix((v, xs)) =>
    val leaves = xs.collect[Free[[X] =>> Graph[V, X], Tree[V]]]{case Fix((s, _)) => Free.Bind(Vertex(s))}
    val branches = xs.collect[Free[[X] =>> Graph[V, X], Tree[V]]]{case x @ Fix((_, cs)) if cs.nonEmpty => Free.Pure(x)}
    val level = Connect(Free.Bind(Vertex(v)),
                        leaves.tail.foldLeft(leaves.head)((t, x) => Free.Bind(Overlay(t, x))))
    if branches.isEmpty then level
    else Overlay(Free.Bind(level),
                 branches.tail.foldRight(branches.head)((x, t) => Free.Bind(Overlay(t, x))))
}

And an example of Fibonacci histo in Python you can translate yourself

def fib_alg(fh):
    if isinstance(fh, Z): return 1
    elif isinstance(fh.pred.fa, Z): return 1
    else: return fh.pred.a + fh.pred.fa.pred.a

For reference, the definitions of futu and cata (chrono being their composite)

def futu[F[_] : Functor, A](coalg: A => F[Free[F, A]])(a: A): Fix[F] =
  def picking(ff: Free[F, A]): Fix[F] = ff match
    case Free.Pure(a) => futu(coalg)(a)
    case Free.Bind(ff) => Fix(ff.map(picking))
  Fix(coalg(a).map(picking))

def histo[F[_] : Functor, A](alg: F[CoFree[F, A]] => A)(fix: Fix[F]): A =
  def bundling(fix: Fix[F]): CoFree[F, A] =
    CoFree(histo(alg)(fix), fix.unFix.map(bundling))
  alg(fix.unFix.map(bundling))

All code here was taken from https://github.com/Adam-Vandervorst/RecursionSchemes

like image 85
Adam Avatar answered Oct 23 '25 14:10

Adam



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!