ScalazのFreeモナドに入門してみる

Scala(Scalaz)のFreeモナドに関して勉強中なのですが、とりあえず現時点までに調べた内容と自分の考えについてまとめてみました。

# 具体的な用途

処理を順次実行するための用途が中心であると思われる。Elixirの[Ecto.Multi](https://hexdocs.pm/ecto/Ecto.Multi.html)にちょっと似ている。([こちら](https://elixirforum.com/t/a-freer-monad-implemented-in-elixir/399/11)のmichalmuskalaさんのコメントを見る限り、Ecto.MultiはFreeモナドのアイデアを参考にしているようである)

(2016/02/05追記)

下記の記事を拝見したのですが、Freeモナドは

> データが末端に来る全ての再帰データ型に使えるモデル

であると理解しておくのがいいのかな？と。

[独習 Scalaz — Stackless Scala with Free Monads](http://eed3si9n.com/learning-scalaz/ja/Stackless+Scala+with+Free+Monads.html)

上記記事からの引用ですが、下記のように色々な再帰的データ構造が簡単に作れるようです。

```scala
type Pair[+A] = (A, A)
type BinTree[+A] = Free[Pair, A]

type Tree[+A] = Free[List, A]

type FreeMonoid[+A] = Free[({type λ[+α] = (A,α)})#λ, Unit]

type Trivial[+A] = Unit
type Option[+A] = Free[Trivial, A]
```

また、下記の記事では

> - Functorがあれば任意のデータ型をMonadにできる
> - Coyonedaを使えば Kind が * のものは、FunctorさえなくてもFree Monadにできる(それをOperational Monadと呼ぶらしい)
ある意味、IOモナドや、Readerモナドの代替というか、それらを組み合わせたような効果を表現できる(?)
> - FreeでDSLを作っておいて、それを実行するinterpreter*4を切り替えることによって、とても綺麗に副作用を切り離すことができたり、副作用*5の実行部分を超柔軟にあとから切り替えることができる(テストなどで便利)。DIフレームワークや、テスト用のモックライブラリなんていらなかったんや！

という説明も記載されていました。

[CoproductとInjectを使ったFree Monadの合成とExtensible Effects - scalaとか・・・](http://d.hatena.ne.jp/xuwei/20140618/1403054751)

# サンプルコード1(インタープリタ方式)

下記のリポジトリに置いてあります。

[kenta-polyglot/freemonad_sample](https://github.com/kenta-polyglot/freemonad_sample)

```scala:Sample1.scala
import scalaz.{Free, Functor, Id, Monad, ~>}

object Sample1 extends App {
  trait Animal[+A]
  case class Human(name: String) extends Animal[String]
  case class Cheetah(kph: Int) extends Animal[Int]
  case class Elephant(weight: Double) extends Animal[Double]
  case class Monster(name: String, kph: Int, weight: Double) extends Animal[(String, Int, Double)]

  def human(name: String): Free[Animal, String] =
    Free.liftF[Animal, String](Human(name))
  def cheetah(kph: Int): Free[Animal, Int] =
    Free.liftF[Animal, Int](Cheetah(kph))
  def elephant(weight: Double): Free[Animal, Double] =
    Free.liftF[Animal, Double](Elephant(weight))
  def monster(name: String, kph: Int, weight: Double): Free[Animal, (String, Int, Double)] =
    Free.liftF[Animal, (String, Int, Double)](Monster(name, kph, weight))

  val interpreter = new (Animal ~> Id.Id) {
    def apply[A](a: Animal[A]): A = a match {
      case Human(name) =>
        println(s"I'm a human. My name is ${name}.")
        name
      case Cheetah(kph) =>
        println(s"I'm a cheetah. I can run at ${kph} kph.")
        kph
      case Elephant(weight) =>
        println(s"I'm an elephant. My weight is ${weight} ton.")
        weight
      case Monster(name, kph, weight) =>
        println(s"I'm a monster. My name is ${name}. I can run at ${kph} kph. My weight is ${weight} ton. hahaha.")
        (name, kph, weight)
    }
  }

  val subs: Free[Animal, String] = for {
    _ <- human("Kenta Katsumata")
    kph <- cheetah(60)
    weight <- elephant(2.5)
    _ <- monster("Hulk", kph, weight)
  } yield (s"complete!")

  val result = subs.foldMap(interpreter)
  println(result)
}
```

実行してみる

```bash
sbt "runMain Sample1"
// I'm a human. My name is Kenta Katsumata.
// I'm a cheetah. I can run at 60 kph.
// I'm an elephant. My weight is 2.5 ton.
// I'm a monster. My name is Hulk. I can run at 60 kph. My weight is 2.5 ton. hahaha.
// complete!

```

# サンプルコード2(Functorとresumeと再帰を使用)

```scala:Sample2.scala
import scalaz.{Free, Functor, Id, Monad, -\/, \/-, ~>}

object Sample2 extends App {
  trait Animal[+A]
  // Functorの制約により、各case classにジェネリクスなメンバを含める必要がある
  case class Human[A](name: String, a: A) extends Animal[A]
  case class Cheetah[A](kph: Int, a: A) extends Animal[A]
  case class Elephant[A](weight: Double, a: A) extends Animal[A]
  case class Monster[A](name: String, kph: Int, weight: Double, a: A) extends Animal[A]
  case class None() extends Animal[Nothing]

  def human(name: String): Free[Animal, String] =
    Free.liftF[Animal, String](Human(name, ""))
  def cheetah(kph: Int): Free[Animal, Int] =
    Free.liftF[Animal, Int](Cheetah(kph, 0))
  def elephant(weight: Double): Free[Animal, Double] =
    Free.liftF[Animal, Double](Elephant(weight, 0.0))
  def monster(name: String, kph: Int, weight: Double): Free[Animal, (String, Int, Double)] =
    Free.liftF[Animal, (String, Int, Double)](Monster(name, kph, weight, ("", 0, 0.0)))

  implicit val animalFunctor = new Functor[Animal] {
    def map[A, B](a: Animal[A])(f: A => B): Animal[B] = a match {
      // いずれかのcase文をコメントアウトするとランタイムエラーが発生する
      case Human(name, a)                => Human(name, f(a))
      case Cheetah(kph, a)               => Cheetah(kph, f(a))
      case Elephant(weight, a)           => Elephant(weight, f(a))
      case Monster(name, kph, weight, a) => Monster(name, kph, weight, f(a))
    }
  }

  val subs: Free[Animal, String] = for {
    _ <- human("Kenta Katsumata")
    kph <- cheetah(60)
    weight <- elephant(2.5)
    _ <- monster("Hulk", kph, weight)
  } yield (s"complete!")

  def run(free: Free[Animal, String]):String =
    free.resume match {
      case \/-(a) => a
      case -\/(Human(name, a)) => // いちいち受け取って・・・
        println(s"I'm a human. My name is ${name}.")
        run(a) // いちいち次の処理に渡す必要がある
      case -\/(Cheetah(kph, a)) =>
        println(s"I'm a cheetah. I can run at ${kph} kph.")
        run(a)
      case -\/(Elephant(weight, a)) =>
        println(s"I'm an elephant. My weight is ${weight} ton.")
        run(a)
      case -\/(Monster(name, kph, weight, a)) =>
        println(s"I'm a monster. My name is ${name}. I can run at ${kph} kph. My weight is ${weight} ton. hahaha.")
        run(a)
      case -\/(_) =>
        "not exists."
    }

  run(subs)
}
```

実行してみる

```bash
sbt "runMain Sample2"
// I'm a human. My name is Kenta Katsumata.
// I'm a cheetah. I can run at 60 kph.
// I'm an elephant. My weight is 2.5 ton.
// I'm a monster. My name is Hulk. I can run at 0 kph. My weight is 0.0 ton. hahaha.
```

# 所感

`Ecto.Multi`のように、処理を一気に順次実行するための用途でFreeモナドを使う場合、Functorとresumeを使う方式は余計なコードが増えてしまうのであまり望ましくないような印象。下記のスライド等を拝見する限り、「**なにかの再帰処理を、綺麗に、安全に、かつスタックオーバーフローを発生させないように書きたい**」という場合、もしくは「**順次処理の途中で処理待ち(ユーザの入力待ち等)が発生する場合**」に、Functorとresumeを使うと便利なのかもしれない。(未検証)

[stackless scala with free monad](http://halcat.org/slide/free/out/#0)

# 備考

参考にさせて頂いたサンプルコードの多くは、Functorとresumeを使う方式で記述されていたのですが、これは旧バージョンのscalazのFreeモナドの各畳み込み関数等の引数が`implicit`な`Functor`を要求していたことによるものなのかもしれません。

[scalaz/Free.scala at series/7.1.x · scalaz/scalaz](https://github.com/scalaz/scalaz/blob/series/7.1.x/core/src/main/scala/scalaz/Free.scala)

バージョン7.3の`foldMap`関数等は、暗黙の`Functor`引数が削除されているので、「順次処理が目的の場合はFunctorいらないよね」みたいな議論があったのかなーみたいな。

# 参考にさせて頂いた資料

[stackless scala with free monad](http://halcat.org/slide/free/out/#0)
[独習 Scalaz — Free Monad](http://eed3si9n.com/learning-scalaz/ja/Free+Monad.html)
[独習 Scalaz — Stackless Scala with Free Monads](http://eed3si9n.com/learning-scalaz/ja/Stackless+Scala+with+Free+Monads.html)
[Freeモナド in Scala - ( ꒪⌓꒪) ゆるよろ日記](http://yuroyoro.hatenablog.com/entry/2012/11/16/171834)
[scalaz - Free - Qiita](http://qiita.com/ryoppy/items/c03b47b34ddfd710809c)
[Free Monads in Scalaz - how to use them - Chris Stucchio](https://www.chrisstucchio.com/blog/2015/free_monads_in_scalaz.html)