Upload
kota-mizushima
View
2.870
Download
0
Embed Size (px)
DESCRIPTION
Scalaのimplicit parameter入門的な何かです。
Citation preview
Implicit Explicit Scala
水島 宏太
(@kmizu)
自己紹介
の前にお約束
JIT(Just In Tsukkomi)歓迎
@kmizu http://twitter.com/kmizu
@ktmizushima (English) http://twitter.com/ktmizushima
id:kmizushima
http://d.hatena.ne.jp/kmizushima
Scala辻斬り ヒーラー
Continuation Workshop 2011 (2011/09/24)
限定継続の人(Tiark Rompf)が来てた
あ、ありのまま(ry
ちなみにこの自己紹介は(ry
今回説明すること
その前にQuiz * 3
ルール:-Xprint:typer禁止
Q.1 A1-A3, B1-B3 を埋めてください
val m = Map("A" -> 1, "B" -> 2)
m.map{case (x, y) => (y, x)}
m.map{case (x, y) => x}
m.map[A1, A2]({case (x, y) => (y, x) })(?:A3)
m.map[B1, B2]{case (x, y) => x })(?:B3)
A.1
val m = Map("A" -> 1, "B" -> 2)
m.map{case (x, y) => (y, x)}
m.map{case (x, y) => x}
m.map[(Int, String), Map[Int, String]]{ case (x, y) => (y, x) }(?:CanBuildFrom[Map[String, Int], String,
Map[Int, String])
m.map[String, Iterable[String]]({ case (x, y) => x })
(?:CanBuildFrom[Map[String, Int], String, Iterable[String]]
Why ?
Q.2 A1, A2, B1, B2 を埋めてください
val l1 = List(1, 2, 3)
val l2 = List(1.5, 2.5, 3.5)
l1.sum
l2.sum
l1.sum[A1](?:A2)
l2.sum[B2](?:B2)
A.2
val l1 = List(1, 2, 3)
val l2 = List(1.5, 2.5, 3.5)
l1.sum
l2.sum
l1.sum[Int](?:Numeric[Int])
l2.sum[Double](?:Numeric[Double])
Why ?
Q.3 A1-A3, B1-B3 を埋めてください
val l1 = List("A" -> 1, "B" -> 2)
val l2 = List(1, 2, 3)
l1.toMap
l2.toMap
l1.toMap[A1, A2](?:A3)
l2.toMap[B1, B2](?:B3)
A.3
val l1 = List("A" -> 1, "B" -> 2)
val l2 = List(1, 2, 3)
l1.toMap
l2.toMap
l1.toMap[String, Int](?: (String, Int) <:< (String, Int))
l2.toMap[B1, B2](?:B3)
→ error: Cannot prove that Int <:< (T, U).
Why ?
3つに共通するもの
それは
Implicit Parameter
Implicit Parameter
最も誤解されてきた機能
実はとても強力な機能
implicit parameter=省略可能引数
とりあえず
implicit parameter
が今回のメイン
A.1~A.3の意味がわかる
ようになる(はず)
基本
def hoge(implicit x: Int) = x + 3
implicit val INT: Int = 3
println(hoge) → println(hoge(INT)) → 6
間違いではないが
全然嬉しくない
基本
trait Addible[A] {
def plus(x: A, y: A): A
def zero: A
}
implicit object IntAddible extends Addible[Int] {
def plus(x: Int, y: Int): Int = x + y
def zero: Int = 0
}
def sum[A](nums: List[A])(implicit addible: Addible[A]): A = {
nums.foldLeft(addible.zero)((x, y) => addible.plus(x, y))
}
println(sum(List(1, 2, 3, 4, 5)))
→ println(sum(List(1, 2, 3, 4, 5))(IntAddible))
implicit宣言された型に適合する
implicitな値を拾ってくれる
implicit探索規則
基本:静的スコープで決まる
メソッド呼び出し時に「直接参照可能な」implicitな値が対象
「直接参照可能な」の意味
1. 同一スコープにimplicitな値が定義されている
2. importによってimplicitな値が導入されている
例外規則
implicit parameterの型に「関連付けられたクラス/トレイト」のコンパニオンオ
ブジェクトも探索対象
「関連付けられたクラス/トレイト」
trait G[A, B, C] の場合:
G, A, B, C のコンパニオンオブジェクトを探索
trait Addible[A] {def plus(x: A, y: A): Adef zero: A
}object Addible { //companion objectimplicit object IntAddible extends Addible[Int] {def plus(x: Int, y: Int): Int = x + ydef zero: Int = 0
}}
def sum[A](nums: List[A])(implicit addible: Addible[A]): A = {nums.foldLeft(addible.zero)((x, y) => addible.plus(x, y))
}
println(sum(List(1, 2, 3, 4, 5)))→ println(sum(List(1, 2, 3, 4, 5))(IntAddible)) // OK
デフォルトで探索対象にして欲しい
implicitな値をコンパニオンオブジェクト
に入れると便利
何が嬉しい?
Doubleもsumしたくなった
implicit object DoubleAddible extends Addible[Int] {def plus(x: Double, y: Double): Double = x + ydef zero: Double = 0.0
}
println(sum(List(1.0, 2.0, 3.0)))→ println(sum(List(1.0, 2.0, 3.0))(DoubleAddible))
元のデータ型に変更を加えずに
データ型の性質を新しく定義
特殊:implicit推論規則
implicitな値から型パラメータを
「逆向きに」推論可能
Scala 2.8で導入
implicit val tupleA: (Int, String) = (1, "A")implicit val tupleB: (Double, String) = (1.5, "B")
def useImplicitTuple2[A, B](value: A)(implicit tuple: (A, B)): B = {tuple._2
}println(useImplicitTuple2(100)) // Aprintln(useImplicitTuple2(1.5)) // B
Aに応じて推論されるBが変わる
2.8コレクションには不可欠
A.1 ~ A.3の定義を
もう一度見てみる
A.1val m = Map("A" -> 1, "B" -> 2)
// def map [B, That] (f: ((A, B)) ⇒ B)
(implicit bf: CanBuildFrom[Map[A, B], B, That]): That
m.map{case (x, y) => (y, x)}
m.map{case (x, y) => x}
m.map[(Int, String), Map[Int, String]]{ case (x, y) => (y, x) }(?:CanBuildFrom[Map[String, Int],
String, Map[Int, String])
m.map[String, Iterable[String]]({ case (x, y) => x })
(?:CanBuildFrom[Map[Int, String], String, Iterable[String]])
引数の型情報から順方向に推論したのではThatはわからない
implicit推論規則による解決
scala.collection.immutable.Map
のコンパニオンオブジェクト
package scala.collection
package immutable
object Map ... {
..
//Coll = Map[_, _]
implicit def canBuildFrom[A, B]:
CanBuildFrom[Coll, (A, B), Map[A, B]] =
new MapCanBuildFrom[A, B]
}
(1) 任意の型A, B についてCanBuildFrom[Map[_, _], (A, B), Map[A, B]]
型のimplicitな値を生成可能
scala.collection.Iterable
のコンパニオンオブジェクト
package scala.collection
...
object Iterable ... {
..
//Coll = Iterable[A]
implicit def canBuildFrom[A]:
CanBuildFrom[Coll, (A), Iterable[A]] =
new GenericCanBuildFrom[A]
}
(2) 任意の型A, B についてCanBuildFrom[Iterable[_, _], (A), Iterable[A]]
型のimplicitな値を生成可能
適用優先度: (1) > (2)
(1)が適合していなくても
(2)が適合すればOK
A.2
val l1 = List(1, 2, 3)
val l2 = List(1.5, 2.5, 3.5)
l1.sum
l2.sum
// def sum [B >: A] (implicit num: Numeric[B]): B
l1.sum[Int](?:Numeric[Int])
l2.sum[Double](?:Numeric[Double])
package scala.math...object Numeric { trait IntIsIntegral extends Integral[Int] {def plus(x: Int, y: Int): Int = x + ydef minus(x: Int, y: Int): Int = x - ydef times(x: Int, y: Int): Int = x * ydef quot(x: Int, y: Int): Int = x / ydef rem(x: Int, y: Int): Int = x % ydef negate(x: Int): Int = -xdef fromInt(x: Int): Int = xdef toInt(x: Int): Int = x.toIntdef toLong(x: Int): Long = xdef toFloat(x: Int): Float = xdef toDouble(x: Int): Double = x
}..// Integeral[T] <: Numeric[T] なので、 Numeric[Int]のimplicitな値も同時に定義// していることになるimplicit object IntIsIntegral extends IntIsIntegral withOrdering.IntOrdering
}
A.3
val l1 = List("A" -> 1, "B" -> 2)
val l2 = List(1, 2, 3)
l1.toMap
l2.toMap
l1.toMap[String, Int](?: (String, Int) <:< (String, Int))
l2.toMap[B1, B2](?:B3)
→ error: Cannot prove that Int <:< (T, U).
名状し難い型名状し難い型名状し難い型
答えはPredefの中に
package scala.math
...
object Predef ... {
@implicitNotFound(msg = "Cannot prove that ${From} <:< ${To}.")
sealed abstract class <:<[-From, +To] extends (From => To) with Serializable
private[this] final val singleton_<:< = new <:<[Any,Any] {
def apply(x: Any): Any = x }
// not in the <:< companion object because it is also
// intended to subsume identity (which is no longer implicit)
implicit def conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]
}
名状し難い型の定義
ポイント
sealed abstract class <:<[-From, +To]
implicit def conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]
任意の型Aについて
A <: A 型のimplicitな値を生成
可能(多相的なimplicit)
名状し難い <:< の
左辺:contravariant (-)
右辺: covariant(+)
def fun[A, B](a: A, b: B)(implicit ev: A <:< B)
A = Int, B = Any
つまり A <: B のとき
(Int <:< Any) なimplicitな値が必要
(Int <:< Int) <: (Int <:< Any) なので
(<:< の右辺はcovariant)
conforms[Int]: Int <: Int で適合する
A = Any, B = Int
つまり A <: B のとき
(Any <:< Int) なimplicitな値が必要
(Any <:< Any) <: (Any <: Int) かつ
(Int <:< Int) <: (Any <: Int)
(<:< の左辺はcontravariant)
→コンパイルエラー
implicit parameterを使った
ライブラリ/フレームワーク
Scala標準ライブラリScalazsjson
ScalaChecksprayAkka
Squeryl...
たくさんある
implicit parameterを
活用してみてください
通称コップ本 第二版 好評発売中
Scala 2.8対応+付録Scala 2.9記事(by @kmizu)
宣伝(2)
こんなキーワードにピンと来たら(ry
Cakeパターン, CONCEPTパターン, 限定継続
現場でのScala, Javaとの連携