怖いScala!

  • 無職\(^o^)/
  • もう少し無職楽しむつもりだけど、なんとなく関数型言語書いて金貰えるような仕事ないかな・・・
  • 今年の前半は働いてたけど、去年の今頃も無職だった
  • 暇だからScalazにひたすらpull req送る
  • 気づいたらコミッターになっていた((((;゚Д゚))))!?
  • 2013年11月現在、コミット数3位(コミットした行数では7位くらい。つまり細かいコミットが多い)

怖いScalaとは・・・

  • 怖い人がいる?
  • implicit、なにそれ怖い?
  • Scalaz怖い?(実はもっと怖いライブラリが・・・)
  • モナド怖い?
  • 関数型怖い?

怖い(?)と思われてるものを真面目に話そう!

怖い人達(?)の話

いい人?

((((;゚Д゚))))ガクガクブルブル

Scalazやモナドの話

  • 7.0.0が2013年4月22日リリース
  • 2013年11月28日現在、stableな最新版は 7.0.5
  • 7.0.0から7.0.5までは(基本的に)バイナリ互換がある
  • 細かいバグ修正、互換性を壊さない機能追加のリリースが5回
  • 現在7.1.0を同時に開発中
  • 7.1.xは7.1.0-M4が最新版
  • 7.1.xは現在マイルストーン版なので、バイナリ互換はない
  • Scala2.11.0.finalがでる頃(2014年初め)に、7.1.0のfinalリリース予定
  • 7.1.0のfinalがでたら、その後の7.1.1や7.1.2はバイナリ互換保つ予定
  • 7.0.xは、Scala2.9.2以上
  • 7.1.xは、Scala2.9.3
  • 7.2.xは、Scala2.9を切り捨てる予定?
  • 7.2.xはまだ開発始まってないので、詳細は未定
  • その他versionについての詳細はwikiを参照
  • https://github.com/scalaz/scalaz/wiki

http://leifwarner.net/scalaz.svg この図は7.0.x系なので、7.1.xと少し違う

モナド!

モナド!

モナド!

モナドを避けているから、必要以上に怖く感じるのでは? モナドを理解してしまおう!

  • 世間には「モナドはコンテナ」とかそういう説明があったりします
  • が、個人的にはそういう「変な比喩」を使った説明好きではない
  • 比喩は理解を助けるかもしれない、が単に余計な混乱を招く可能性

では、モナドとはなにか?

単なる型クラスの1つ

では、型クラスとは?

  • Scala標準ライブラリでも使われてる
  • Scalaz以外の多くのライブラリでも使われている

型クラスの定義 https://github.com/scala/scala/blob/v2.10.3/src/library/scala/math/Ordering.scala

trait Ordering[T]{
  def compare(x: T, y: T): Int
}
上記のコードは色々省略してあります

型クラスのインスタンスの定義

https://github.com/scala/scala/blob/v2.10.3/src/library/scala/math/Ordering.scala#L250-L256

trait IntOrdering extends Ordering[Int] {
  def compare(x: Int, y: Int) =
    if (x < y) -1
    else if (x == y) 0
    else 1
}
implicit object Int extends IntOrdering

型クラスを使う側 https://github.com/scala/scala/blob/v2.10.3/src/library/scala/collection/TraversableOnce.scala#L214

def max[B >: A](implicit cmp: Ordering[B]): A
  • implicitは型クラスのためのもの!
  • implicitは型クラス以外にも使えてしまう
  • 型クラスは素晴らしいので、どんどん使いましょう
  • 型クラス以外のimplicitの使いすぎに注意
  • そのimplicitは、型クラスとしての使い方なのか、そうでないのか意識しよう

ほとんどの型クラスには「満たすべき法則がある」

法則とは?

  • 例えるなら、JavaでequalshashCodeが特定の法則を満たさないといけないようなもの
  • 先ほどのOrderingならば、a > b かつ b > c ならば a > c など(推移律)

モナドの型クラスの定義

trait Monad[F[_]]{
  def bind[A, B](fa: F[A])(f: A => F[B]): F[B]
  def point[A](a: => A): F[A]
}

モナド則とは

return a >>= k  ==  k a
 m >>= return  ==  m
 m >>= (\x -> k x >>= h)  ==  (m >>= k) >>= h

Haskellではあれなので、Scalazにおける例

モナド則その1

https://github.com/scalaz/scalaz/blob/v7.1.0-M4/core/src/main/scala/scalaz/Monad.scala#L71

def leftIdentity[A, B](a: A, f: A => F[B])(implicit FB: Equal[F[B]]): Boolean =
  FB.equal(bind(point(a))(f), f(a))

モナド則その2

https://github.com/scalaz/scalaz/blob/v7.1.0-M4/core/src/main/scala/scalaz/Monad.scala#L73

def rightIdentity[A](a: F[A])(implicit FA: Equal[F[A]]): Boolean =
  FA.equal(bind(a)(point(_: A)), a)

モナド則その3

https://github.com/scalaz/scalaz/blob/v7.1.0-M4/core/src/main/scala/scalaz/Bind.scala#L40-L41

def associativeBind[A, B, C](fa: F[A], f: A => F[B], g: B => F[C])(implicit FC: Equal[F[C]]): Boolean =
  FC.equal(bind(bind(fa)(f))(g), bind(fa)((a: A) => bind(f(a))(g)))

もうみなさん、モナドを理解しましたね!

提案:

  • 「モナドはなにか?」

ということと

  • 「モナドはどのように役に立つのか?」
  • 「具体的にどのようなモナドがあるのか?」

を分けて考えよう!

  • 「モナドはなにか?」と言われたら、いま説明したもの以外のなにものでもない。
  • 「具体的にモナドになるもの」は大量にあるので、それはゆっくり覚えていけばよい。

型クラスの継承関係

  • 型クラスにも“継承“という概念が存在する
  • ここからモナドと他の型クラスの継承関係について

Monadの親の型クラス

  • InvariantFunctor (これはめったに使わないので忘れてもいいです)
  • Functor
  • Apply
  • Bind
  • Applicative

ScalazとHaskellと違い

  • Haskellの標準ライブラリには Functor, Applicative, Monad の3つのみ
  • Haskellの場合それら3つは継承関係ではない
  • Haskellの標準ライブラリには存在しない Apply, Bind がScalazにはある
  • Haskellの標準ライブラリにはSemigroupはない
  • その他いろいろ
  • Apply ならば、必ず Functor になる
  • Applicative ならば、必ず Apply になる
  • Bind ならば、必ず Apply になる
  • Monad ならば、必ず Applicative になる
  • Monad ならば、必ず Bind になる
  • Bind にはなるが Monad にはならない例が存在する(Mapなど)
  • Applicaive にはなるが Monad にはならない例( scalaz.Validation など)

よくある誤解

  • Monadではない、むしろMonadと呼ぶには間違っている(単なるApplicativeの例に対して)Monadという言葉を使う
  • Monadではなく、MonadPlusが関係することに関してMonadという言葉を使う。
  • 参考 HaskellのdoとScalaのfor式とEitherとMonadPlus

マサカリが飛んでくるかもしれません?

  • 先ほど説明したように、一般的にMonadが満たすべき性質は3つ
  • しかし、ScalazのMonadは多くの他の型クラスを継承している
  • よって、親の型クラスの性質も満たさなければならない
  • すべて合わせると13個になる!!!
  • しかし、デフォルト実装を使っていれば必ず満たすので、普通は気にする必要ない
  • モナドという概念自体は、これまで説明したようなものでそれほど複雑ではない
  • しかしscalaz.Monadを含めた様々な型クラスを使ったり、新たに定義する場合には色々な知識が必要

また、Monad と同時に、それらと関連する型クラスである Functor なども同時に理解するようにしたほうがいい

Scalazを使いたくてもどこから使っていいかわからない人向けの話

  • scalaz.Validation
  • Applicative#sequence

なぜ Validation がScalaz初心者にオススメなのか?

  • Monadにならないが、Applicativeになる例
  • 実用的!
  • ついでに、Semigroup も関連する
  • Scalaz独自のデータ型である NonEmptyList の利点が生かせる
  • 似たようなものを再実装するくらいなら、最初から既存のもの使おう!
  • 再実装するとしても、コード読むことで参考になるはず

Scala2.10から入ったFutureを使う場合

  • Option[Future[A]]Future[Option[A]] にしたい

とかよくあるはず

Scalaz7.0.xの場合は、scala.concurrent.Futureの型クラスのインスタンスは入ってないので以下の設定が必要

libraryDependencies ++= Seq(
  "org.typelevel" %% "scalaz-contrib-210" % "0.1.5",
  "org.scalaz" %% "scalaz-core" % "7.0.5"
)

scalaVersion := "2.10.3"
import scala.concurrent.Future
import scalaz._
import Scalaz._
import scalaz.contrib.std.scalaFuture._ // 7.0.xの場合
import scalaz.std.scalaFuture._ // 7.1.0-M4の場合
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

Option(Future(1)).sequence // Future(Option(1))

A[B[C]]

という型のオブジェクトがあったとき、以下のどちらかであれば

  • AがTraverse、BがApplicative
  • AがTraverse1、BがApply

A[B[C]] から B[A[C]] に変換できる

おわり。ありがとうございました