Scala 2.12のゆるふあな話をするよ！(Partial Unification for Type Constructor Inference)

遅くなりましたが、この記事は[Scala Advent Calendar 2016](http://qiita.com/advent-calendar/2016/scala)の12/9です。

「ゆるふあに」ということで本当は2.12での`Either`の右傾(right-biased)化の話をしようと思っていたのですが、前日に`Either`の変更点については紹介されていたのでもっとゆるふあな2.12の話をします！

## の前に1点だけEitherの話を
`Either`に`flatMap`が定義されたことによって、以下のコードが通るようになりました。

```scala
val ei: Either[Int, String] = for {
  a <- Right((42, "xxx")).right
  (b, c) = a
  d <- Right(0.1).right
} yield s"$c: ${b + d}"
```

このコード、2.12では`.right`が無くても全く同じ結果なので2.11でも通りそうな気がしますが、「`Either`には`flatMap`なんてないよ！」というコンパイルエラーになります。
なぜかは謎です。ゆるふあです。

`Either`には`withFilter`が定義できないので、パターンマッチが使えないという困った感じでした。

それが`2.12`ではエラーメッセージの通り`Either`に`flatMap`が定義されたため、上記コードがコンパイル可能になっています。うれしいですね。

# ゆるふあな2.12の新機能(型コンストラクターの推論に対する部分的単一化)について
こちらの機能、まだコンパイルオプションに`-Y`系である`-Ypartial-unification`を設定しないと実行できません。  
ゆるふあですね。

build.sbtに記述を追加しましょう。
この機能は高階型と共に使う機能なので、一緒にhigherKindsオプションも入れてしまいましょう。
(本番のコードでは、使用する部分だけ`scala.language.higherKinds`をインポートするほうがお薦めです。)

あと、今回はscalazを使用してサンプルを書いているので、試してみるなら入れておいてください。

```scala:build.sbt
scalacOptions ++= Seq(
  "-Ypartial-unification",
  "-language:higherKinds"
)

libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.2.8"
```

さて、サンプルなので`sbt console`を起動して簡単にscalazの`Functor#map`を再定義します。

```scala:2.12
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._

scala> def map[F[_], A, B](fa: F[A])(f: A => B)(implicit F: Functor[F]) = F.map(fa)(f)
map: [F[_], A, B](fa: F[A])(f: A => B)(implicit F: scalaz.Functor[F])F[B]

scala> map(Option(42))(a => (a + 1).toString)
res1: Option[String] = Some(43)
```

ここまでは`2.11`系と同じですね。
これに、Eitherを渡してみましょう。

```scala:2.12
scala> map(Option(42).toRight("left"))(a => (a + 1).toString)
res2: scala.util.Either[String,String] = Right(43)
```

なんと！普通に動くのです！！！

バージョンを`2.11`に戻して同じコードを貼り付けて見ましょう。

`2.11`のscalazは`2.12`のものと比べて非常にダウンロードに時間がかかりますね・・・。
あと、そのままbuild.sbt上でscalaのバージョンだけ下げたら`-Ypartial-unification`なんてオプションはないぞ！と起こられました。

```scala:2.11
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._

scala> def map[F[_], A, B](fa: F[A])(f: A => B)(implicit F: Functor[F]) = F.map(fa)(f)
map: [F[_], A, B](fa: F[A])(f: A => B)(implicit F: scalaz.Functor[F])F[B]

scala> map(Option(42))(a => (a + 1).toString)
res0: Option[String] = Some(43)

scala> map(Option(42).toRight("left"))(a => (a + 1).toString)
<console>:19: error: type mismatch;
 found   : Product with Serializable with scala.util.Either[String,Int]
 required: ?F[?A]
Note that implicit conversions are not applicable because they are ambiguous:
 both method ToAssociativeOps in trait ToAssociativeOps of type [F[_, _], A, B](v: F[A,B])(implicit F0: scalaz.Associative[F])scalaz.syntax.AssociativeOps[F,A,B]
 and method ToBitraverseOps in trait ToBitraverseOps of type [F[_, _], A, B](v: F[A,B])(implicit F0: scalaz.Bitraverse[F])scalaz.syntax.BitraverseOps[F,A,B]
 are possible conversion functions from Product with Serializable with scala.util.Either[String,Int] to ?F[?A]
       map(Option(42).toRight("left"))(a => (a + 1).toString)
```

おわかりでしょうか？  
`Either[A, B]`は型パラメーターが2つあるので`F[_, _]`であって、引数の`F[_]`には合わないということです。

-----
2016/12/14 追記
これをコンパイルが通るようにするには以下のように記述する必要があります。

```scala:2.11
scala> type StringEither[A] = Either[String, A]
defined type alias StringEither

scala> val a: StringEither[Int] = Option(42).toRight("left")
a: StringEither[Int] = Right(42)

scala> map(a)(x => (x + 1).toString)
res1: StringEither[String] = Right(43)
```

このように`StringEither[A]`という型を作ってその型で渡すか、以下のように`f[A]`という型を一時的に定義して、その`f`を`F[_]`に渡すと明示する必要があります。

```scala:2.11
scala> map[({ type f[A] = Either[String, A] })#f, Int, String](Option(42).toRight("left"))(a => (a + 1).toString)
res2: scala.util.Either[String,String] = Right(43)
```

-----

`2.12`で追加された`-Ypartial-unification`は、複数の型パラメーターを持つ型のインスタンスが来た時に、勝手に目的の型にあう形になるまで左側を固定してくれるのです。
ついにという感じですね。

```scala
例1:
// 受け取り側の型
F[A]
// 渡す側の型
SomeType[Int, String, Byte]
// => 解釈
F[_] = SomeType[Int, String, _]
A = Byte

例2:
// 受け取り側の型
G[A, B]
// 渡す側の型
SomeType2[Int, Char, List[String], Byte]
// => 解釈
G[_, _] = SomeType2[Int, Char, _, _]
A = List[String]
B = Byte
```

受け取る側の型に合うまで左から順に埋められていくので、Aの部分に当たるようになるのは常に右側です。
あくまで関数呼び出しの解釈途中でだけで、このまま型パラメーター指定ができるようになったわけではないので注意が必要です。

# ゆるふあ機能の使い道
使い道としては、モナドトランスフォーマーとかで型指定をしなくてもうまくいくようになった辺りかなぁと。

```scala:2.11
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._

scala> val a: Either[Int, Option[String]] = Right(Some("foo"))
a: Either[Int,Option[String]] = Right(Some(foo))

scala> val b = OptionT(a)
<console>:18: error: no type parameters for method apply: (run: F[Option[A]])scalaz.OptionT[F,A] in object OptionT exist so that it can be applied to arguments (Either[Int,Option[String]])
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : Either[Int,Option[String]]
 required: ?F[Option[?A]]
       val b = OptionT(a)
               ^
<console>:18: error: type mismatch;
 found   : Either[Int,Option[String]]
 required: F[Option[A]]
       val b = OptionT(a)
                       ^
```

```scala:2.12
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._

scala> val a: Either[Int, Option[String]] = Right(Some("foo"))
a: Either[Int,Option[String]] = Right(Some(foo))

scala> val b = OptionT(a)
b: scalaz.OptionT[[+B]Either[Int,B],String] = OptionT(Right(Some(foo)))
```

便利ですね！！