ワンランク上の関数型技法による Command パターンの置き換え

["Functional Programming Patterns in Scala and Clojure"](https://www.amazon.co.jp/Functional-Programming-Patterns-Scala-Clojure/dp/1937785475)という本([PDF](https://doc.lagout.org/programmation/Functional%20Programming%20Patterns%20in%20Scala%20and%20Clojure_%20Write%20Lean%20Programs%20for%20the%20JVM%20%5BBevilacqua-Linn%202013-11-02%5D.pdf))に、オブジェクト指向のデザインパターンやイディオムを関数型の技法で置き換える11個のイディオムが紹介されている。

ただ出版から4年くらい経ってて少し古いし、そもそもOOPメインのプログラマ向けに初歩のFP技法を導入するような本の趣旨なので、ある程度`Scala`に慣れてくると置き換え結果のコードが若干物足りなくなる。

そこで、その中の一つの「Replacing Command（Commandパターンの置き換え）」を題材に、初級よりはやや難しめと思われるFP技法でさらに書き換えてみた。

道具立てとしては、Writer モナド、State モナド、Eff モナド、IO モナド、Free モナド、モナドトランスフォーマー、Akkaなどを使った。

### ライブラリ等
下記あたり。

```
compilerPlugin("org.scalameta" % "paradise" % "3.0.0-M10" cross CrossVersion.full),
compilerPlugin("org.spire-math" %% "kind-projector" % "0.9.4"),
"org.typelevel" % "cats-core_2.12" % "1.0.0-RC1",
"org.typelevel" % "cats-free_2.12" % "1.0.0-RC1",
"org.typelevel" %% "cats-effect" % "0.5",
"org.scalacheck" %% "scalacheck" % "1.13.4" % "test",
"org.atnos" %% "eff" % "5.0.0-20171107064756-b526890",
"com.typesafe.akka" %% "akka-typed" % "2.5.8"
```

## 書籍オリジナル
本に載ってた以下の`Scala`コードをお題にした。

```scala
class CashRegister(var total: Int) {
  def addCash(toAdd: Int) {
    total += toAdd
  }
}

object Register {
  def makePurchase(register: CashRegister, amount: Int) = {
    () => {
      println(s"Purchase in amount: $amount")
      register.addCash(amount)
    }
  }

  var purchases: Vector[() => Unit] = Vector()
  def executePurchase(purchase: () => Unit) = {
    purchases = purchases :+ purchase
    purchase()
  }
}
```

`Command`パターンの構成要素と以下のように対応する。
- `CashRegister`：`Client`
- `Register`：`Invoker`
- `Register#makePurchase`が返す`()=>Unit`型の関数：`Command`
GoFの`Command`パターンと微妙な差異があるが、「要求をオブジェクト化」して履歴として持ったりすることなどは共通。

書籍とほぼ同じように、これを以下のようにして動かす。

```scala
val register = new CashRegister(0)

val p1 = makePurchase(register, 100)
val p2 = makePurchase(register, 50)

executePurchase(p1)
executePurchase(p2)
register.total

register.total = 10
purchases.foreach(_())
register.total
```
- 残高ゼロのキャッシュレジスターを生成する
- このキャッシュレジスターを参照する購入関数オブジェクトを２個作る
- 関数オブジェクトを実行すると、購入と残高が出力されると共に、履歴が記録される
- キャッシュレジスターの残高を適当に変更する
- 履歴を再実行すると改めて購入の様子とともに再計算された残高が出力される


以下のようなコンソール出力になる。

```
Purchase in amount: 100
res0: Unit = ()
Purchase in amount: 50
res1: Unit = ()
res2: Int = 150

register.total: Int = 10
Purchase in amount: 100
Purchase in amount: 50
res3: Unit = ()
res4: Int = 160
```

## 書き直した結果

本では敷居の高い"purely functional"な技法を避けて"more pragmatic"な手法を取ったとあり、副作用を許容したコードなっている。ただ最近では関数型の技法も普及していることもあるので、ここではもうちょっと進んだ技法を使って純粋関数型なコーディングに寄せてみる。まず Writerモナドから。

### Writerモナド
- `Vector`へのミュータブルな参照で保持していた購入履歴を、`Writer`モナドに持たせるようにした。
- `var`で`total`を持っていた`CashRegister`をイミュータブルにした。

```scala
case class CashRegister(total: Int) {
  def addCash(toAdd: Int) = CashRegister(total + toAdd)
}
type Purchase = CashRegister => CashRegister
def makePurchase(amount: Int): Purchase = (r: CashRegister) => {
  println(s"Purchase in amount: $amount")
  r addCash amount
}
type PurchaseHistory = Vector[Purchase]
def execute(purchase: Purchase)
    : CashRegister => Writer[PurchaseHistory, CashRegister] =
  register => purchase(register).writer(Vector(purchase))
```

以下のコードで、オリジナルと同等の動作結果が得られる。

```scala
val p1 = makePurchase(100)
val p2 = makePurchase(50)

def program(r0: CashRegister) = for {
  r1 <- execute(p1)(r0)
  r2 <- execute(p2)(r1)
} yield r2

val (history, total1) = program(CashRegister(0)).run

// 再実行
history.foldLeft(CashRegister(10))((acc, p) => p(acc))
```
"impure"な変数はなくなったが、`execute`関数呼び出しの間で`CashRegister`を連鎖的に受け渡しているのがよくない。これを次に直す。

[gist --- by writer monad](https://gist.github.com/yasuabe/b38caf105591b61126db1d96fcd6b12a)

### Stateモナド
購入の実行ごとに更新される`CashRegister`を、Vector[Purchase]で表した購入履歴と共に`State`に持たせるようにした。

```scala
case class CashRegister(total: Int) {
  def addCash(toAdd: Int) = CashRegister(total + toAdd)
}
type Purchase = CashRegister => CashRegister
def makePurchase(amount: Int): Purchase = (r: CashRegister) => {
  println(s"Purchase in amount: $amount")
  r addCash amount
}
type PurchaseHistory = Vector[Purchase]
def execute(p: Purchase): State[(PurchaseHistory, CashRegister), Unit] =
  State { case (h, r) => ((h :+ p, p(r)), ()) }
```

以下のように動かす

```scala
val p1 = makePurchase(100)
val p2 = makePurchase(50)

def program = for {
  _ <- execute(p1)
  _ <- execute(p2)
} yield ()

val (history, total1) = program.runS((Vector.empty, CashRegister(0))).value

// 再実行
history.foldLeft(CashRegister(10))((acc, p) => p(acc))
```
`CashRegister`の連鎖的な受け渡しは解消されたが、Vector.emptyを渡すことになったのがやや残念。`Writer` モナドの時は`Vector`が `Monoid`として扱われていたので、単位元を明示的に指定する必要がなかった。微妙な修正だが、次はこれを`Eff`モナドを使って直してみる。

[gist --- by state monad](https://gist.github.com/yasuabe/7c173a6ce908802cab3ad05266a29d56)

### Eff モナド

購入履歴を`Write`に、最後の`CashRegister`を`State`に持たせて、２種類のモナドを合成してみた。モナドトランスフォーマーを使うこともできたが、ここでは`Eff`を使った。

```scala
case class CashRegister(total: Int) {
  def addCash(toAdd: Int) = CashRegister(total + toAdd)
}
type Purchase = CashRegister => CashRegister
def makePurchase(amount: Int): Purchase = (r: CashRegister) => {
  println(s"Purchase in amount: $amount")
  r addCash amount
}
type Stack = Fx.fx2[State[CashRegister, ?], Writer[Purchase, ?]]
type _writerVector[R]  = Writer[Purchase, ?]    |= R
type _stateRegister[R] = State[CashRegister, ?] |= R
def execute[R :_stateRegister :_writerVector](p: Purchase): Eff[R, Unit] = for {
  _ <- tell(p)
  _ <- modify(p(_))
} yield ()
```

以下のように動かす.

```scala
val p1 = makePurchase(100)
val p2 = makePurchase(50)

def program = for {
  _ <- execute[Stack](p1)
  _ <- execute[Stack](p2)
} yield ()

val ((_, last), history) = program.runState(CashRegister(0)).runWriter.run

// 再実行
val total2 = history.foldLeft(CashRegister(10))((acc, p) => p(acc))
```
`Writer`と`State`の両方のいいところが使えるようになった。（ただしこの例だと複雑性がまして難度が高くなったデメリットの方が大きいかもしれない。)

[gist --- by eff monad](https://gist.github.com/yasuabe/24826581af9a804334139a9f0488e9c8)

### IOモナド

関数型プログラミングの観点で言えば、`Purchase`が`println`を呼んでいるのもそもそも気になる。`IO`モナドと`State`をモナドトランスフォーマーで組み合わせて`unsafe`なコードを局所化してみる。

```scala
case class CashRegister(total: Int) {
  def addCash(toAdd: Int) = CashRegister(total + toAdd)
}
type Purchase = CashRegister => IO[CashRegister]
def makePurchase(amount: Int): Purchase = (r: CashRegister) => for {
  _ <- IO { println (s"Purchase in amount: $amount") }
} yield r addCash amount

type PurchaseHistory = Vector[Purchase]
def execute(p: Purchase): StateT[IO, (PurchaseHistory, CashRegister), Unit] =
  StateT { case (h, r) => p(r) map { r2 => ((h :+ p, r2), ()) }}
```

以下のコードでこれまで同様の動きとなる。`StateT`から取り出した`IO`を、ここでは`unsafeRunSync `で実行しているが、他にも各種`run`メソッドが提供されていて、状況に応じて選択できる。

```scala
val p1 = makePurchase(100)
val p2 = makePurchase(50)

def program = for {
  _ <- execute(p1)
  _ <- execute(p2)
} yield ()

val io1 = program.runS((Vector.empty, CashRegister(0)))
val (history, last) = io1.unsafeRunSync()

val io2 = history.foldLeft(IO.pure(CashRegister(10)))(_ flatMap _)
io2.unsafeRunSync()
```
`println`を`IO`モナドに包むことはできたが、モナドが`IO`に固定されているのがまだいまいち。また`makePurchase`はドメインのコードなので、入出力や並列関連など「実行」にまつわるコードは切り離して、「関心事の分離」を徹底したい。

[gist --- by io monad](https://gist.github.com/yasuabe/c9e6368d1103ac79c8aabcc0219caab5)

### Free モナド
- `PrintLn`を含む`Console`という`ADT`をリフト関数と共に定義。
- `makePurchase`では、`println`の直接呼び出しでもIOモナドにくるんだ形でもなく、`ADT`を`Free`にリフトして返すようにした。

```scala
sealed trait Console[A]
case class   PrintLn(s: String) extends Console[Unit]

type ConsoleF[T] = Free[Console, T]
def printLn(s: String): ConsoleF[Unit] = liftF(PrintLn(s))

case class CashRegister(total: Int) {
  def addCash(toAdd: Int) = CashRegister(total + toAdd)
}
type Purchase = CashRegister => ConsoleF[CashRegister]
def makePurchase(amount: Int): Purchase = r => for {
  _ <- printLn(s"Purchase in amount: $amount")
} yield r addCash amount

type PurchaseHistory = Vector[Purchase]
def execute(p: Purchase): StateT[ConsoleF, (PurchaseHistory, CashRegister), Unit] =
  StateT { case (h, r) => p(r) map { r2 => ((h :+ p, r2), ()) }}
```

動かすコードは以下。基本的には同じ動きだが、ADTを解釈するインタープリター（自然変換）を、IO用とID用の２種類書いた。

```scala
val p1 = makePurchase(100)
val p2 = makePurchase(50)

def freeProgram = (for {
  _ <- execute(p1)
  _ <- execute(p2)
} yield ()).runS((Vector.empty, CashRegister(0)))

def ioCompiler: (Console ~> IO) = new (Console ~> IO) {
  override def apply[A](fa: Console[A]): IO[A] = fa match {
    case PrintLn(text) => IO { println(text) }
  }
}
val (history, last) = freeProgram.foldMap(ioCompiler).unsafeRunSync()

// 再実行
def idCompiler: (Console ~> Id) = new (Console ~> Id) {
  override def apply[A](fa: Console[A]): Id[A] = fa match {
    case PrintLn(text) => println(text); ()
  }
}
history.foldLeftM(CashRegister(10))((b, p) => p(b)).foldMap(idCompiler)
```

ドメインのコードから「どのように動かすか」を分離して、後付けで任意のモナドを選択できるようにした。

[gist --- by free monad](https://gist.github.com/yasuabe/fdb780d6c533603c25694a75aeea3cbb)

### Akka typed

「データと手続きの一体化」のようなOOPバイアスを一旦カッコ入れしてちょっと考え直してみると、`Purchase`が振る舞いを持って`CashRegister`を更新するのも不自然だし、`Purchase`の責務ではないとすれば`CashRegister`自身の責務のような気もする（OOP的にも）。

以下では、`Purchase`を代数的データ型`Command`に属する、単なる一つデータコンテナとして定義して、`CashRegister`に振る舞いを移動し、client(`CashRegister`)とinvokerの「協調」を Akka Actorの通信として表現してみる。

```scala
trait Command
final case class Purchase(amount: Int) extends Command
case object PrintTotal extends Command

object CashRegister {
  def apply(total: Int): Behavior[Command] = behavior(total)
  private def behavior(total: Int): Behavior[Command] = immutable {
    case (_, Purchase(amount)) => println(amount); behavior(total + amount)
    case (_, PrintTotal)       => println(total); Actor.same
  }
}
type PurchaseHistory = Vector[Purchase]
def execute(p: Purchase)
  : ReaderT[WriterT[IO, PurchaseHistory, ?], ActorRef[Command], Unit] =
  ReaderT { actor =>
    WriterT.lift[IO, PurchaseHistory, Unit](IO { actor ! p }) tell Vector(p)
  }
```
Actor以外の道具立てとしては、ReaderT, WriterT, IOをモナドトランスフォーマーで組み合わせている。`WriterT`にはこれまでと同じように購入履歴を持たせているが、`Reader`には`ActorRef` を載せている。また`IO`を使って記述と実行を分離している。

以下のコードでこれまでとほぼ同じ動作になるが、`CashRegister`の残額を表示するのに`PrintTotal`コマンドを送信しているところが少し違う。

```scala
val p1 = Purchase(100)
val p2 = Purchase(50)

def program = for {
  _ <- execute(p1)
  _ <- execute(p2)
} yield ()

val root = Actor.deferred[Nothing] { ctx =>

  val (h, _) = program.run(register).run.unsafeRunSync()
  register ! PrintTotal

  val register2 = ctx.spawn(CashRegister(1), "register2")
  h.foreach(a => register2 ! a)
  register2 ! PrintTotal

  Actor.empty
}
ActorSystem[Nothing](root, "RegisterSample")
```

[gist --- by akka typed](https://gist.github.com/yasuabe/c83ead2e76c93686c5da1c664b340863)

### TODO
- tagless final、super monadの試行。
- 書籍中の他のOOP置き換えパターンでも同様にやってみること。