Recursion:

schemes, algebras, finally tagless,

data types

Alexander Konovalov, Compellon Inc

alex.knvl@gmail.com @alexknvl

再帰:スキーム,代数,finally tagless,データ型. 統合されたビジョン

The goal

  • Briefly go over type isomorphisms and type algebra.
  • Show the connection between final tagless, "initial encoding", and recursion schemes.
  • Brief introduction to Free monads.
  • Show the connection between Free monads, MTL, and Final Tagless.

final tagless、initial encoding、再帰スキームのつながりを証明する

Free モナド、MTL、final tagless のつながりを証明する

Isomorphisms

​A and B are isomorphic when there exist two morphisms,

to: A → B and from: B → A, such that both

 

from (to a) = a

and

to (from b) = b

同型写像: 条件を満たす 2つの射が存在する

Type Isomorphisms

Can't be faithfully implemented except in Dependently typed languages (Idris, Agda, Coq).

trait Iso[A, B] {
  def to(a: A): B
  def from(b: B): A
  def fromTo(x: A): (from(to(x))).type =:= x.type
  def toFrom(x: B): (to(from(x))).type =:= x.type
}

Iso を忠実に型で表現するには依存型言語が要る

trait Iso[A, B] {
  def to(a: A): B
  def from(b: B): A
 
  // make sure that (to andThen from) == id
  // and (from andThen to) == id
}

So let's just manually check the necessary laws.

仕方が無いので手動で公理をチェックする

type A = Either[Boolean, Unit]
sealed trait B
case object One extends B
case object Two extends B
case object Three extends B

Are these isomorphic?

同型写像なのか?

def to(a: A): B = a match {
  case Left(true)  => One
  case Left(false) => Two
  case Right(())   => Three
}

def from(b: B): A = b match {
  case One   => Left(true)
  case Two   => Left(false)
  case Three => Right(())
}

Yes, here is an isomorphism:

同型写像だ

type A = Boolean => Boolean
sealed trait B
case object One   extends B
case object Two   extends B
case object Three extends B
case object Four  extends B

Are these isomorphic?

Conceptually, a function A ⇒ B can be represented as a tuple (B, B, ...) ("A" times)

A ⇒ B 関数は「A回」続く (B, B, ...) というタプルで表現できる

同型写像なのか?

def to(f: A): B = (f(true), f(false)) match {
  case (false, false) => One
  case (false, true)  => Two
  case (true,  true)   => Three
  case (true,  true)   => Four
}

def from(b: B): A = b match {
  case One   => x => if (x) false else false
  case Two   => x => if (x) false else true
  case Three => x => if (x) true  else true
  case Four  => x => if (x) true  else true
}

Yes.

そうだ

type A = Boolean => Unit
type B = Nothing => Int
type C = Unit

Are these isomorphic?

Consider the "tuple representation" of the above two functions.

One is a tuple of two units,

and the other is an empty tuple of ints.

同型写像なのか?

関数のタプル表現を考えてみよう

最初のは 2つの Unit を持ち、次のは空の Int のタプル

def to(f: Boolean => Unit): Nothing => Int = 
  (a: Nothing) => a
def from(f: Nothing => Int): Boolean => Unit = 
  (a: Boolean) => ()

def to(f: Nothing => Int): Unit = ()
def from(f: Unit): Nothing => Int = 
  (a: Nothing) => a

But if we construct the necessary functions...

We don't seem to be really using the arguments!

Is there some other way to understand what is going on?

しかし、実際に必要な関数を作ってみると...

実際には引数は使っていない!

他に理解の手助けとなる方法は無いだろうか?

A surprising detour

  • When are two things equal?

 

1729

12³+1

"the first number expressible as a sum of two cubes in two different ways"

 

  • When are two things equal?

意外な回り道

2つのものが等しいのは、どういうときだろう?

1729、「2組の異なる 2つの3乗数の和として表すことができる最初の数」

Gottfried Wilhelm Leibniz, 1646-1716:

For any x and y, if x is identical to y, then x and y have all the same properties.

For any x and y, if x and y have all the same properties, then x is identical to y.

x と y が同一ならば、x と y は全ての属性が同じである

x と y の全ての属性が同じならば、x と y は同一である

x = y ⟺ ∀ P. P x ↔ P y

Where P is quantified over all properties of an object.

ここで P はあるオブジェクトの全ての属性によって量化される

x = y ⟺ ∀ P. P x ↔ P y

If 1729 and 12³+1 are expressions, they are different.

sealed trait Expr
case class Lit(i: Int) extends Expr
case class Pow(a: Int, b: Int) extends Expr
case class Add(a: Int, b: Int) extends Expr

val x = Lit(1729)
val y = Add(Pow(12, 3), 1)

これらを式で表すと、別物の扱いとなる

x = y ⟺ ∀ P. P x ↔ P y

If 1729 and 12³+1 are integers, they are the same.

type Expr = Int

val x = 1729
val y = 12 * 12 * 12 + 1

これらを整数で表すと、同じものとなる

Type Isomorphisms

type A = Boolean => Unit
type B = Nothing => Int

def to(f: Boolean => Unit): Nothing => Int = 
  (a: Nothing) => a
def from(f: Nothing => Int): Boolean => Unit = 
  (a: Boolean) => ()

Are these isomorphic?

A と B は同型だろうか?

def property(f: Boolean => Unit): Boolean = ???

property { _ => () }
property { case true => ()
           case false => () }

def property(f: Nothing => Int): Boolean = ???

property(_ => 1)
property(_ => 2)
property(x => x)

What properties of them can we observe?

None!

どんな属性か観測できますか?

観測不能!

We can't see the difference of them

as values within a pure functional language.

We can see the difference between

the actual expression trees.

We don't really care about the latter as since it does not affect the semantics.

どんな属性か観測できますか?

実装上の式の構文木違いは分かる

実行時に意味論に影響は無いのでそれは無視しても良い

type A = Boolean => Unit
type B = Nothing => Int

def to(f: Boolean => Unit): Nothing => Int = 
  (a: Nothing) => a
def from(f: Nothing => Int): Boolean => Unit = 
  (a: Boolean) => ()

So are these isomorphic? Yes, both from the counting perspective, and from the Identity of Indiscernibles argument.

型を数える手法でも引数の同一性を見る手法でも、A と B は同型

An interesting result:

 

If A and B are isomorphic,
(a₁: A) and (a₂: A) map to (b₁: A) and (b₂: A) respectively,
then a₁ = a₂ ⟺ b₁ = b₂.

In other words, discernible objects are mapped to discernible objects.

Equal objects are mapped to equal objects.

区別のつくオブジェクトは区別のつくオブジェクトへと写像し、等しいオブジェクトは等しいオブジェクトへと写像する

If A and B are isomorphic,
(a₁: A) and (a₂: A) map to (b₁: A) and (b₂: A) respectively,
then a₁ = a₂ ⟺ b₁ = b₂.

a₁ = a₂

to a₁ = to a₂

b₁ = b₂

from b₁ = from b₂

Type Algebra

A few more important isomorphisms:

 

(a, b) ≈ (b, a)
(a, (b, c)) ≈ ((a, b), c)
(a, Either[b, c]) ≈ Either[(a, b), (a, c)]
Either[a, b] ≈ Either[b, a]
Either[Nothing, a] ≈ a
(Unit, a) ≈ a

重要な型同型

(a, b) ≈ (b, a)
(a, (b, c)) ≈ ((a, b), c)
(a, Either[b, c]) ≈ Either[(a, b), (a, c)]
Either[a, b] ≈ Either[b, a]
Either[Nothing, a] ≈ a
(Unit, a) ≈ a

What if we replace Either with +, (,) with *, Unit with 1, and Nothing with 0?..

Either を和、タプルを積、Unit を 1、Nothing を 0 と置き換える

(a, b) ≈ (b, a)
(a, (b, c)) ≈ ((a, b), c)
(a, Either[b, c]) ≈ Either[(a, b), (a, c)]
Either[a, b] ≈ Either[b, a]
Either[Nothing, a] ≈ a
(Unit, a) ≈ a

a * b ≈ b * a
a * (b * c) ≈ (a * b) * c
a * (b + c) ≈ a * b + a * c
a + b ≈ b + a
0 + a ≈ a
1 * a ≈ a

Amusingly, we get something very similar to the regular algebra of natural numbers...

面白いことに、自然数の代数系と似通っている

(a + b) c ≈ (a c, b c)
a b c ≈ (a, b) c

def curry[A, B, C](f: A => B => C): ((A, B)) => C =
    { case (a, b) => f(a)(b) }

def uncurry[A, B, C](f: ((A, B)) => C): A => B => C =
    a => b => f((a, b))

def undiag[A, B, C](f: Either[A, B] => C): (A => C, B => C) =
    (a => f(Left(a)), b => f(Right(b)))

def diag[A, B, C](f: (A => C, B => C)): Either[A, B] => C =
    { case Left(a)  => f._1(a)
      case Right(b) => f._2(b) }

A couple very important results about functions:

関数の重要な結果

(a + b) c ≈ (a c, b c)
a b c ≈ (a, b) c

c^(a + b) ≈ c^a * c^b
(c^b)^a ≈ c ^ (a * b)

Turns out that similarly to how Either is + and (,) is *, functions correspond to exponentiation:

関数は指数に対応する

Type Algebra

Summing it all up:

Either[a, b] is +

Tuple2[a, b] or (a, b) is A * B

Function1[a, b] or (a → b) is b ^ a

Nothing is 0

Unit is 1

Bool is 2

型代数のまとめ

One more important theorem that we will need.

a ≈ b, then f a ≈ f b for any functor f

Including phantom, covariant, contravariant, or invariant functors.

必要であろうもう一つの重要な定理

全てのファンクターについて、a ≈ b のとき f a ≈ f b が成り立つ

phantom, covariant など変化球ファンクターなどでも成り立つ

a ≈ b, then f a ≈ f b for any functor f

to' = map to

from' = map from

map to . map from =

map (to . from) = map id = id

Just one more thing

We will be using the following type synonym to save some horizontal space:

type ⊥ = Nothing

ページの都合上 ⊥ をもって Nothing とする

Let's talk recursion

Say we have a data type

sealed trait List[+A]
case object  Nil                             extends List[⊥]
case class   Cons[A](head: A, tail: List[A]) extends List[A]
def foldr[A, Z](this: List[A])(seed: Z)(combine: (A, Z) => Z): Z = 
  this match {
    case Nil        => seed
    case Cons(h, t) => combine(h, t.foldr(seed)(combine))
  }

and we would like to fold over it

再帰について話そう

sealed trait List[+A]
case object  Nil                             extends List[⊥]
case class   Cons[A](head: A, tail: List[A]) extends List[A]

def foldr[A, Z](this: List[A])(seed: Z)(combine: (A, Z) => Z): Z = 
  this match {
    case Nil        => seed
    case Cons(h, t) => combine(h, t.foldr(seed)(combine))
  }

Why do we want fold?

Turns out that every function on list can be written in terms of foldr!

def sum(list: List[Int]): Int = 
  foldr(list)(0)(_ + _)
def map[A, B](list: List[A])(f: A => B): List[B] = 
  foldr(list)(Nil)((h, t) => Cons(f(h), t))
def isEmpty[A](list: List[A]): Boolean = 
  foldr(list)(true)((_, _) => false)

リストの全ての関数は foldr で表現できる!

Let's look at a slightly more realistic example:

sealed trait Expr
case class Lit(i: Int)           extends Expr
case class Add(l: Expr, r: Expr) extends Expr
case class Mul(l: Expr, r: Expr) extends Expr

def foldr[Z](this: Expr)(lit: Int => Z)(add: (Z, Z) => Z)(mul: (Z, Z) => Z): Z = 
  this match {
    case Lit(i)    => lit(i)
    case Add(l, r) => add(foldr(l)(lit)(add)(mul), foldr(r)(lit)(add)(mul))
    case Mul(l, r) => mul(foldr(l)(lit)(add)(mul), foldr(r)(lit)(add)(mul))
  }

We can reinterpret our expressions in different ways:

val expr = Mul(Lit(3), Add(Lit(2), Lit(3))

def eval(expr: Expr): Int = 
  foldr(expr)(i => i)(_ + _)(_ * _)
val res = eval(expr) // 3 * (2 + 3) = 15

def print(expr: Expr): String = 
  foldr(expr)(i => i.toString)((l, r) => s"($l + $r)")((l, r) => s"($l * $r)")
val str = print(expr) // "(3 * (2 + 3))"

もう少し現実的な例を見てみよう

\forall z. \text{Expr} \rightarrow (Int \rightarrow z) \rightarrow ((z, z) \rightarrow z) \rightarrow ((z, z) \rightarrow z) \rightarrow z
def foldr[Z](this: Expr)(lit: Int => Z)(add: (Z, Z) => Z)(mul: (Z, Z) => Z): Z = 
  this match {
    case Lit(i)    => lit(i)
    case Add(l, r) => add(l.foldr(lit)(add)(mul), r.foldr(lit)(add)(mul))
    case Mul(l, r) => mul(l.foldr(lit)(add)(mul), r.foldr(lit)(add)(mul))
  }

Let's look at the type of foldr:

\forall z. \text{Expr} \rightarrow ((Int + (z, z) + (z, z)) \rightarrow z) \rightarrow z

Let's combine the arguments together:

There are two important concepts here, an algebra, and a pattern/signature functor.

foldrの型について

Let's talk algebras

In mathematics, and more specifically in abstract algebra, an algebraic structure on a set A (called carrier set or underlying set) is a collection of finitary operations on A; the set A with this structure is also called an algebra.

代数について話そう

In mathematics, specifically in category theory, F-algebras generalize algebraic structure. Rewriting the algebraic laws in terms of morphisms eliminates all references to quantified elements from the axioms, and these algebraic laws may then be glued together in terms of a single functor F, the signature.

Let's talk algebras

代数について話そう

\mathfrak{F} c \rightarrow c ~~~ - ~~~ \text{an }\mathfrak{F}\text{-algebra}
\mathfrak{F} - \text{is called a signature, or pattern functor} \\ c - \text{is called a carrier}
type Algebra[F[_], C] = F[C] => C
def foldr[Z](this: Expr)(lit: Int => Z)(add: (Z, Z) => Z)(mul: (Z, Z) => Z): Z = 
  this match {
    case Lit(i)    => lit(i)
    case Add(l, r) => add(l.foldr(lit)(add)(mul), r.foldr(lit)(add)(mul))
    case Mul(l, r) => mul(l.foldr(lit)(add)(mul), r.foldr(lit)(add)(mul))
  }
\forall z. \text{Expr} \rightarrow ((Int + (z, z) + (z, z)) \rightarrow z) \rightarrow z
sealed trait ExprF[+Z]
case class LitF(i: Int)        extends ExprF[⊥]
case class AddF[Z](l: Z, r: Z) extends ExprF[Z]
case class MulF[Z](l: Z, r: Z) extends ExprF[Z]
\forall z. \text{Expr} \rightarrow (\text{ExprF}~z \rightarrow z) \rightarrow z

Let's define the signature functor.

ファンクターのシグネチャを定義しよう

def foldRS[A, Z](algebra: ExprF[Z] => Z): Z = 
  this match {
    case Lit(i)    => algebra(LitF(i))
    case Add(l, r) => algebra(AddF(foldRS(l)(algebra), foldRS(r)(algebra)))
    case Mul(l, r) => algebra(MulF(foldRS(l)(algebra), foldRS(r)(algebra)))
  }
sealed trait ExprF[+Z]
case class LitF(i: Int)        extends ExprF[⊥]
case class AddF[Z](l: Z, r: Z) extends ExprF[Z]
case class MulF[Z](l: Z, r: Z) extends ExprF[Z]
\forall z. \text{Expr} \rightarrow (\text{ExprF}~z \rightarrow z) \rightarrow z

We have arrived at Recursion Schemes.

再帰スキームに到達した

Let's talk Recursion Schemes

\begin{aligned} \text{Expr} &= \text{Lit}~\text{Int} | &\text{Add}~ &\text{Expr} &\text{Expr} &| &\text{Mul}~ &\text{Expr} &\text{Expr} \\ \text{ExprF} \bullet &= \text{Lit}~\text{Int} | &\text{Add} &~~~\bullet &\bullet~~~ &| &\text{Mul} &~~~\bullet &\bullet~~~ \end{aligned}

ExprF represents one layer of the Expr tree.

Here's another way to look at signature functors:

再帰スキームについて話そう

def project(expr: Expr): ExprF[Expr] = expr match {
  case Lit(i)    => LitF(i)
  case Add(x, y) => AddF(x, y)
  case Mul(x, y) => MulF(x, y)
}

def embed(expr: ExprF[Expr]): Expr = expr match {
  case LitF(i)    => Lit(i)
  case AddF(x, y) => Add(x, y)
  case MulF(x, y) => Mul(x, y)
}
def foldRS[Z](this: Expr)(algebra: ExprF[Z] => Z): Z = 
  this match {
    case Lit(i)    => algebra(LitF(i))
    case Add(l, r) => algebra(AddF(l.foldRS(algebra), r.foldRS(algebra)))
    case Mul(l, r) => algebra(MulF(l.foldRS(algebra), r.foldRS(algebra)))
  }

We can use these to rewrite

as

def foldRS[Z](this: Expr)(algebra: ExprF[Z] => Z): Z = 
  algebra(project(this).map(subtree => foldRS(subtree)(algebra)))

書き直す

とこうなる

trait Recursive[T, F[_]] extends Functor[F] {
  def project(t: T): F[T]
  def cata[Z](t: T)(algebra: F[Z] => Z): Z = 
    algebra(project(t).map(cata(_)(algebra)))
}

new Recursive[Expr, ExprF] {
  def map[A, B](e: ExprF[A])(f: A => B): ExprF[B] = ...
  def project(t: Expr): ExprF[Expr] = ...
}

We can further generalize this idea of extracting one layer of a recursive data structure at a time.

This is the basis of Recursion Schemes.

ファンクターは再帰的なデータ構造を一度に一つの層に抽出する考えに一般化出来る

再帰スキームの基本

val expr = Mul(Lit(3), Add(Lit(2), Lit(3))

val evalAlg: ExprF[Int] => Int = {
  case LitF(i)    => i
  case AddF(l, r) => l + r
  case MulF(l, r) => l * r
}
val res = foldRS(expr)(evalAlg) // 3 * (2 + 3) = 15

val printAlg: ExprF[String] => String = {
  case LitF(i)    => i.toString
  case AddF(l, r) => s"($l + $r)"
  case MulF(l, r) => s"($l * $r)"
}
val str = foldRS(expr)(printAlg) // "(3 * (2 + 3))"

We can use the new foldRS to reinterpret expressions the same way we could with foldr.

foldrと同じようにfoldRSを再解釈式に利用できる

Alright, let's go back to our types again:

What if instead of extracting a pattern functor, we extract the algebra?

\forall z. \text{Expr} \rightarrow (Int \rightarrow z) \rightarrow ((z, z) \rightarrow z) \rightarrow ((z, z) \rightarrow z) \rightarrow z
\forall z. \text{Expr} \rightarrow ((Int + (z, z) + (z, z)) \rightarrow z) \rightarrow z
\forall z. \text{Expr} \rightarrow (Int \rightarrow z, (z, z) \rightarrow z, (z, z) \rightarrow z) \rightarrow z

戻ってみよう

ファンクターの抽出パターンに置き換えるなら、代数を抽出出来るか

What if instead of extracting a pattern functor, we extract the algebra?

trait ExprA[Z] {
  def lit(i: Int): Z
  def add(l: Z, r: Z): Z
  def mul(l: Z, r: Z): Z
}
\forall z. \text{Expr} \rightarrow (Int \rightarrow z, (z, z) \rightarrow z, (z, z) \rightarrow z) \rightarrow z
∀ z. \text{Expr} \rightarrow \text{ExprA}~z \rightarrow z
def foldAlg(expr: Expr)(algebra: ExprA[Z]): Z = expr match {
  case Lit(i)    => algebra.lit(i)
  case Add(l, r) => algebra.add(foldAlg(l)(algebra), foldAlg(r)(algebra))
  case Mul(l, r) => algebra.mul(foldAlg(l)(algebra), foldAlg(r)(algebra))
}

ファンクターの抽出パターンに置き換えるなら、代数を抽出出来るか

val expr = Mul(Lit(3), Add(Lit(2), Lit(3))

val evalAlg: ExprA[Int] = new ExprA[Int] {
  def lit(i: Int): Int = i
  def add(l: Int, r: Int): Int = l + r
  def mul(l: Int, r: Int): Int = l * r
}
val res = foldAlg(expr)(evalAlg) // 3 * (2 + 3) = 15

val printAlg: ExprA[String] = new ExprA[String] {
  def lit(i: Int): String = i.toString
  def add(l: String, r: String): Int = s"($l + $r)"
  def mul(l: String, r: String): Int = s"($l * $r)"
}
val str = foldAlg(expr)(printAlg) // "(3 * (2 + 3))"

We can use the new foldAlg to reinterpret expressions the same way we could with foldr and foldRS

This is the basis of Final Tagless, except usually ExprA is a typeclass.

foldrやfoldRSと同じようにfoldAlgを再解釈式に利用できる

trait ExprC[Z] {
  def lit(i: Int): Z
  def add(l: Z, r: Z): Z
  def mul(l: Z, r: Z): Z
}

def lit[Z](i: Int)(implicit Z: ExprC[Z]): Z = Z.lit(i)
def add[Z](l: Z, r: Z)(implicit Z: ExprC[Z]): Z = Z.add(l, r)
def mul[Z](l: Z, r: Z)(implicit Z: ExprC[Z]): Z = Z.mul(l, r)
def foldFT[Z: ExprC](expr: Expr): Z = expr match {
  case Lit(i)    => lit(i)
  case Add(l, r) => add(foldFT(l), foldFT(r))
  case Mul(l, r) => mul(foldFT(l), foldFT(r))
}

Rewriting foldAlg using a typeclass

型クラスを使ってfoldAlgに書き直す

val expr = Mul(Lit(3), Add(Lit(2), Lit(3))

implicit val evalAlg: ExprC[Int] = new ExprC[Int] {
  def lit(i: Int): Int = i
  def add(l: Int, r: Int): Int = l + r
  def mul(l: Int, r: Int): Int = l * r
}
val res = foldFT[Int](expr) // 3 * (2 + 3) = 15

implicit val printAlg: ExprC[String] = new ExprC[String] {
  def lit(i: Int): String = i.toString
  def add(l: String, r: String): Int = s"($l + $r)"
  def mul(l: String, r: String): Int = s"($l * $r)"
}
val str = foldFT[String](expr) // "(3 * (2 + 3))"

We can use the new foldFT to reinterpret expressions the same way we could with foldr, foldRS, and foldAlg.

foldrやfoldRS、foldAlgと同じようにfoldFTを再解釈式に利用できる

Let's see all of the derived functions

def foldr  [Z](expr: Expr)(lit: Int => Z)(add: (Z, Z) => Z)(mul: (Z, Z) => Z): Z

def foldRS [Z](expr: Expr)(         algebra: ExprF[Z] => Z): Z
def foldAlg[Z](expr: Expr)(         algebra: ExprA[Z]     ): Z
def foldFT [Z](expr: Expr)(implicit algebra: ExprC[Z]     ): Z

Simply by transforming a function type we have arrived at Recursion Schemes and Final Tagless.

派生した関数を見てみよう

type Algebra[F[_], C] = F[C] => C

sealed trait ExprF[+Z]
case class LitF(i: Int)        extends ExprF[⊥]
case class AddF[Z](l: Z, r: Z) extends ExprF[Z]
case class MulF[Z](l: Z, r: Z) extends ExprF[Z]

type ExprA1[Z] = Algebra[ExprF, Z]

trait ExprA2[Z] {
  def lit(i: Int): Z
  def add(l: Z, r: Z): Z
  def mul(l: Z, r: Z): Z
}

These are just different ways to express the same idea: abstracting over recursion.

再帰を超えた抽象化

One more thing

I mentioned before that every function of a recursive type can be expressed in terms of foldr.

Turns out that every type is isomorphic to its Boehm-Berarducci (also known as Church-) encoding.

再帰型の全ての関数はfoldrの用語で表せる

Boehm-Berarducciエンコーディングによって全ての型は同一型に表せる

One more thing

\forall z. \text{Expr} \rightarrow (Int \rightarrow z) \rightarrow ((z, z) \rightarrow z) \rightarrow ((z, z) \rightarrow z) \rightarrow z
\forall z. \text{Expr} \rightarrow ((Int + (z, z) + (z, z)) \rightarrow z) \rightarrow z
\forall z. \text{Expr} \rightarrow (Int \rightarrow z, (z, z) \rightarrow z, (z, z) \rightarrow z) \rightarrow z

This is almost an an isomorphism, we just need to move the quantifier.

ほとんどの同型写像。量化子へ移るのに必要

One more thing

\text{Expr} \leftrightarrow \forall z. (Int \rightarrow z) \rightarrow ((z, z) \rightarrow z) \rightarrow ((z, z) \rightarrow z) \rightarrow z
\text{Expr} \leftrightarrow \forall z. ((Int + (z, z) + (z, z)) \rightarrow z) \rightarrow z
\text{Expr} \leftrightarrow \forall z. (Int \rightarrow z, (z, z) \rightarrow z, (z, z) \rightarrow z) \rightarrow z

This is an isomorphism!

Boehm-Berarducci encoding

Boehm-Berarducciエンコーディング

trait ExprA[Z] {
  def lit(i: Int): Z
  def add(l: Z, r: Z): Z
  def mul(l: Z, r: Z): Z
}

trait Encoded {
  def apply[Z](algebra: ExprA[Z]): Z
}

def to(e: Expr): Encoded = new Encoded {
  def apply[Z](algebra: ExprA[Z]): Z = foldAlg(e)(algebra)
}
def from(e: Encoded): Expr = e.apply[Expr](new ExprA[Expr] {
  def lit(i: Int): Expr = Lit(i)
  def add(l: Expr, r: Expr): Expr = Add(l, r)
  def mul(l: Expr, r: Expr): Expr = Mul(l, r)
})

We can explicitly construct the isomorphism:

同型写像を明示的に構築

In mathematics, an initial algebra is an initial object in the category of F-algebras for a given endofunctor F.

We won't go into detail, but in our case, Expr is the initial algebra carrier of ExprA.

The important takeaway is: a recursive type is isomorphic to ∀ z. Alg z → z,

where Alg z = PatF z → z.

数学では、始代数は、与えられた自己関手Fに対するF-代数の圏における始対象である

ExprはExprAの始対象の集合である

重要な取り除き

Another, more general way of thinking about this:

Every type T is isomorphic to the set of all observations we can make about T.

If you know Yoneda Lemma, you should get some pretty interesting ideas right about now!

これらの考えられる一般的な別の方法

Every type T is isomorphic to the set of all observations we can make about T.

Given some type T, we can construct a type U that represents all observable properties of T as a tuple of their values.

u : T → U
u = x ↦ (p₁ x, p₂ x, ...)

リストの全ての関数は foldr で表現できる!

全ての型Tは同型写像であり、作成できるTについて全ての観測できるセットである

u : T → U
u = x ↦ (p₁ x, p₂ x, ...)

It is clear that it must map equal elements of T to equal elements of U. Identity of Indiscernibles also tells us that distinct elements will map to distinct elements in both directions.

Which means that this mapping is an isomorphism!

不可識別者同一の原理

この写像は、同型写像である

sealed trait Free[F[_], A]
case class Pure   [F[_], A](value: A)             extends Free[F, A]
case class Suspend[F[_], A](value: F[Free[F, A]]) extends Free[F, A]

implicit def monad[F[_]: Functor]: Monad[Free[F, ?]] = ...

def runFree[F[_], A, G[_]: Monad](ffa: Free[F, A])(nat: F ~> G): G[A] = ...

Let's talk Free monads

Free[F, A] is either a pure value without any effects, or a "free computation" suspended within an effect F.

They can be interpreted into any monad G, as long as you supply a way to transform any F[X] into G[X]​.

フリーモナドについて話そう

The reason Free monads are useful is that they allow us to write an expression in a DSL and have it reinterpret into a different monad.

Consider the following API:

get : (key: Int) => Int

set: (key: Int, value: Int) => Unit

Let's talk Free monads

フリーモナドについて話そう

Freeモナドについて話そう

def program: Free[ServiceF, Int] = for {
  x <- get(0)
  _ <- set(0, x + 1)
  _ <- set(1, x + 2)
} yield x

Using Free, we can write a program like this:

runFree[ServiceF, Int, IO](program)(
  new (ServiceF ~> IO) { ... }
) : IO[Int]

And have it interpreted either in IO monad against a real service (e.g. a microservice):

runFree[ServiceF, Int, State[(Int, Int), ?]](program)(
  new (ServiceF ~> State[(Int, Int), ?]) { ... }
) : State[(Int, Int), Int]

Or mock the microservice and run everything in State monad:

Freeを使うとこのように書ける

そして、実際のサービスに対して IO モナドの中で実行するか

実際のサービスをモックするような State モナドで全て動かせる

sealed trait ServiceF[+A]
case class Get[A](key: Int,             k: Int => A) extends ServiceF[A]
case class Set[A](key: Int, value: Int, k: ()  => A) extends ServiceF[A]

def get(key: Int)            : Free[ServiceF, Int]  = Suspend(Get(key,        Pure))
def set(key: Int, value: Int): Free[ServiceF, Unit] = Suspend(Set(key, value, Pure))

and then an interpreter for those operations:

We first define our operations:

new (ServiceF ~> State[(Int, Int), ?]) {
  def apply[X](value: ServiceF[X]): State[(Int, Int), ?] = {
    case Get(key, k) => for {
      state <- State.get
      value = key match {
        case 0 => state._1
        case 1 => state._2
      }
    } yield k(value)

    case Set(key, value, k) => ...
  }
}

操作の定義

これらの操作は、こう解釈される

Usually, people use a slightly different version of Free monads that removes the Functor constraint on F.

sealed trait Free[F[_], A]
case class Pure   [F[_], A]   (value: A)           extends Free[F, A]
case class Suspend[F[_], A]   (value: F[A])        extends Free[F, A]
case class FlatMap[F[_], A, B](value: Free[F, A], 
                               f: A => Free[F, B]) extends Free[F, B]

implicit def monad[F[_]]: Monad[Free[F, ?]] = ...

def runFree[F[_], A, G[_]: Monad](ffa: Free[F, A])(nat: F ~> G): G[A] = ...

It is a bit cleaner but not substantially different.

Fにおけるファンクターの制約を取り除いたものと、Freeモナドの違い

sealed trait ServiceF[A]
case class Get(key: Int,           ) extends ServiceF[Int]
case class Set(key: Int, value: Int) extends ServiceF[Unit]

def get(key: Int)            : Free[ServiceF, Int]  = Suspend(Get(key       ))
def set(key: Int, value: Int): Free[ServiceF, Unit] = Suspend(Set(key, value))

and then an interpreter for those operations:

Similarly, we first define our operations:

new (ServiceF ~> State[(Int, Int), ?]) {
  def apply[X](value: ServiceF[X]): State[(Int, Int), ?] = {
    case Get(key) => for {
      state <- State.get
      value = key match {
        case 0 => state._1
        case 1 => state._2
      }
    } yield value

    case Set(key, value) => ...
  }
}

初めに定義したものと似ている

これらの操作は、こう解釈される

Remember the principle from before:

Every type T is isomorphic to the set of all observations we can make about T.

Let's apply it to our Free[F, A].

The only thing we can observe about (ffa: Free[F, A]) are the results of runFree(ffa).

前の原則を思い出してほしい

Freeを適用してみよう

実行結果だけが観測できる

The only thing we can observe about (ffa: Free[F, A]) are the results of runFree(ffa).

trait Free[F[_], A] {
  def runFree[G[_]: Monad](nat: F ~> G): G[A]
}

implicit def monad[F[_]]: Monad[Free[F, ?]] = ...

def runFree[F[_], A, G[_]: Monad](ffa: Free[F, A])(nat: F ~> G): G[A] 
  = ffa.runFree(nat)

Surprisingly, this works perfectly fine, all instances are fairly straight-forward to implement.

実行結果だけが観測できる

驚くべきことに明確で公平に単刀直入に実装されている

// data Is a b = Is (forall (f :: * -> *). f a -> f b)

trait Is[A, B] {
  def subst[F[_]](fa: F[A]): F[B]
}
object Is {
  implicit def refl[A]: A Is A = new Is[A, A] {
    def subst[F[_]](fa: F[A]): F[A] = fa
  }
}

GADTs = ADTs + equality constraints

THIS SLIDE IS NEW

sealed trait Foo[X]
final case class A() extends Foo[Int]
final case class B(i: Int) extends Foo[Unit]
final case class C[A](i: A) extends Foo[A]

// is the same as

sealed trait Foo[X]
final case class A[X]()      (implicit ev: X Is Int)  extends Foo[X]
final case class B[X](i: Int)(implicit ev: X Is Unit) extends Foo[X]
final case class C[A](i: A)                           extends Foo[A]

GADTs = ADTs + equality constraints

THIS SLIDE IS NEW

trait Free[F[_], A] { def runFree[G[_]: Monad](nat: F ~> G): G[A] }

Once again, we look at the type more closely:

\forall g. \text{Monad}~g \rightarrow (\forall x. f x \rightarrow g x) \rightarrow g a

Let's rewrite this term as a constraint on g

\forall g. \text{Monad}~g \rightarrow A g \rightarrow g a
A g = \forall x. f x \rightarrow g x

Hmm, what is A?

もう一度、もっと近くで型を見てみよう

gによる束縛による条件で書き直して見よう

Aは何?

Consider the concrete case of ServiceF

sealed trait ServiceF[A]
case class Get(key: Int,           ) extends ServiceF[Int]
case class Set(key: Int, value: Int) extends ServiceF[Unit]

trait A[G[_]] {
  def run[X](value: ServiceF[X]): G[X]
}
A g = \forall x. \text{ServiceF}~x \rightarrow g x
\forall x. ((x = \text{Int}, Int) + (x = \text{Unit}, (Int, Int)) \rightarrow g x

Expanding the GADT as a sum of products with type equalities:

ServiceFの具体的なケースで考えてみる

型同一性と直積の和のようなGADTに展開される

\forall x. ((x = \text{Int}, Int) + (x = \text{Unit}, (Int, Int)) \rightarrow g x
\forall x. ((x = \text{Int}, Int) \rightarrow g x, (x = \text{Unit}, (Int, Int)) \rightarrow g x)

Uncurrying:

(\forall x. (x = \text{Int}, Int) \rightarrow g x, \forall x. (x = \text{Unit}, (Int, Int)) \rightarrow g x)

Distributing ∀:

Applying type equalities:

(Int \rightarrow g~\text{Int}, (Int, Int) \rightarrow g~\text{Unit})

逆カリー化

分配

型同一性を適用

sealed trait ServiceF[A]
case class Get(key: Int,           ) extends ServiceF[Int]
case class Set(key: Int, value: Int) extends ServiceF[Unit]

trait A[G[_]] {
  def get(key: Int): G[Int]
  def set(key: Int, value: Int): G[Unit]
}
(Int \rightarrow g~\text{Int}, (Int, Int) \rightarrow g~\text{Unit})

So the constraint A turned out to be what is colloquially known as an algebra!

Aの制約は代数として知られているものと判明した

Let's rewrite our Free monad accordingly:

trait Free[F[_], A] { 
  def runFree[G[_]: Monad](nat: F ~> G): G[A]
}

trait FreeA[Alg[_[_]], A] { 
  def runFree[G[_]: Monad](alg: Alg[G]): G[A]
}

Does it look familiar?

This is Final Tagless encoding!

Freeモナドに書き直そう

タグレスファイナルエンコーディング

trait ServiceAlg[G[_]] {
  def get(key: Int): G[Int]
  def set(key: Int, value: Int): G[Unit]
}
def program: FreeA[ServiceAlg, Int] = ...

Now notice that if we have

we might as well dispose of the FreeA type and write simply:

def program[G[_]: Monad](alg: ServiceAlg[G]): G[Int] = ...

where

FreeA型の置き換えと単純に書き換える

Or we could make ServiceAlg a typeclass:

def program[G[_]: Monad: ServiceAlg]: G[Int] = ...

So what is the takeaway here?

または ServiceAlg 型クラスを作ることもできる

取り除いたのは何か

So what is the takeaway here?

Free = Final Tagless, at least on the the conceptual level.

Both give us an ability to write our programs in a DSL and have them compile into some different language.

There is an isomorphism that converts from one representation to the other

少なくとも概念的レベルで

DSLで書ける力があり、違う言語でもコンパイル出来る

ある表現から別の表現への同型写像

取り除いたのは何か

Summing it all up

folds ≈ Recursion Schemes ≈ Final Tagless (simple algebras)

Identity of Indiscernibles and its connection to isomorphisms

Type algebra

まとめ

Summing it all up

Free ≈ Final Tagless

Isomorphism to the Initial algebra carrier

Isomorphism to the product of all observable properties

まとめ

Q&A

Alexander Konovalov, Compellon Inc

alex.knvl@gmail.com @alexknvl

alexknvl.com

Copy of Recursion

By Alexander Konovalov

Copy of Recursion

  • 1,099
Loading comments...

More from Alexander Konovalov