Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Phantom Type in Scala

5,913 views

Published on

Phantom Type自体の説明はほとんどありません。Implicit parameterを使って、コンパイル時にごにょごにょやる方法を通し、型の表現によっていろいろできることを説明しています。

Published in: Technology

Phantom Type in Scala

  1. 1. Phantom Type in Scala ファントムタイプ社の社長にみんなでPhatom Typeを教える会 2012.10.06 前田康行(@maeda_)
  2. 2. 自己紹介 •  前⽥田康⾏行行 (@maeda_ )•  好きな⾔言語 •  Scala •  Smalltalk•  DyNagoya (Dynamic language + Nagoya) •  http://dynagoya.info
  3. 3. About Phatom Type A phantom type is a parametrised type whoseparameters do not all appear on the right-handside of its definition- Haskell wiki より抜粋 -http://www.haskell.org/haskellwiki/Phantom_type
  4. 4. 幽霊型の例 •  型パラメータTはcase classのフィールドに使われて いないcase class A[T](x : Int)scala> A[String](3)res0: A [String] = A(3)scala> A [List[Char]](3)res1: A [List[Char]] = A(3)scala> res0 == res1res2: Boolean = true•  特にScalaの場合、Type Erasureにより実⾏行行時に型パ ラメータは消える
  5. 5. Phantom Typeは幽霊なのか? •  Phantom Type(幽霊型)は •  実⾏行行時ではなく、コンパイル時にのみ暗躍する。 •  条件を満たさないと、コンパイルエラーにして実⾏行行 できなくする呪いをかける •  コンパイルが通ると成仏していなくなる•  これは幽霊ですね
  6. 6. Phatom Typeの実用例 1 Type Safeな構文木 •  Expr[T]は評価結果がT型となることを表すtrait Expr[T]case object True extends Expr[Boolean]case object False extends Expr [Boolean]case class Number(x: Int) extendsExpr [Int]case class Plus(x1: Expr [Int], x2:Expr [Int]) extends Expr [Int]scala> Plus(Plus(Number(3),Number(4)),Number(5))res4: Plus = Plus(Plus(Number(3),Number(4)),Number(5))scala> Plus(Plus(Number(3), Number(4)), True) // PlusにBooleanは渡せない<console>:14: error: type mismatch; found : True.type required: Expr[Int] Plus(Plus(Number(3), Number(4)), True)
  7. 7. Phatom Typeの実用例 2 状態を表す(定義) sealed trait Stateclass Empty private () extends Stateclass Ready private () extends Stateclass Dinner[Oeuvre <: State, MainDish <: State, Dessert <: State] (){ def cookSalad = new Dinner[Ready, MainDish, Dessert] def cookSoup = new Dinner[Ready, MainDish, Dessert] def cookSteak = new Dinner[Oeuvre, Ready, Dessert] def cookCake = new Dinner[Oeuvre, MainDish, Ready] def serve() = println("Now you can eat!")}object Dinner{ def start = new Dinner[Empty, Empty, Empty]}
  8. 8. Phatom Typeの実用例 2 状態を表す(実行例) scala> Dinner.startres0: Dinner[Empty, Empty, Empty] = Dinner@4b401150scala> Dinner.start.cookSalad.cookCakeres1: Dinner[Ready, Empty, Ready] = Dinner@6d9e4f58scala> Dinner.start.cookSalad.cookCake.serve()Now you can eat!scala> Dinner.start.cookSalad.cookSteak.cookSoup.serve()Now you can eat!
  9. 9. ただのタグじゃなくて、コンパイルエラーにしたい
  10. 10. そこで Implicit Parameter(暗黙のパラメータ) ですよ
  11. 11. implicit parameterの例 // implicit parameter付きの関数の宣⾔言scala> def func(implicit ev: Int) = m// implicit parameterが⾒見見つからないのでエラーscala> func<console>:9: error: could not find implicit value for parameterm: Int// スコープ内にimplicitで宣⾔言した値があると、⾃自動的に渡されるscala> implicit val v = 3scala> func // → 3 •  スコープ内のimplicitで宣⾔言された値の中で、 型が合う値をコンパイル時に探索する
  12. 12. 多相的なimplicit parameter scala> def implicitly[T](implicit ev: T) = mscala> implicit val v = 3scala> implicitly[Int]         // → 3scala> implicitly[String]      // →  コンパイルエラー •  implicitlyメソッドはPredefに標準で定義されている •  型パラメータで指定された型のimplicitに取得できる 値を探索
  13. 13. もう少しお行儀よく class Tag[A]()def func[A](implicit ev: Tag[A]) = "OKimplicit val intAllowed = new Tag[Int]scala> func[Int] // → OKscala> func[String] // →コンパイルエラー •  普通に使う型をimplicitで宣⾔言するのはよくない •  implicitで使⽤用するための型を⽤用意して、その型 パラメータで特定の型を指定する⽅方がいい
  14. 14. 型パラメータを2つにする class Tag[A, B] ()def func[A, B](implicit ev: Tag[A, B]) = "OK"implicit val intAllowed = new Tag[Int, Int]implicit val stringAllowed = new Tag[String, String]scala> func[Int, Int] // OKscala> func[Int, String] // コンパイルエラー •  個別に使⽤用可能な型の組み合わせをimplicitで宣 ⾔言する
  15. 15. 任意の2つの型が同じである ことを表現したい •  型パラメータを持った値は扱えない implicit val x[A] = new Tag[A, A]•  メソッドなら型パラメータを持てる ○ implicit def f[A] = new Tag[A, A]
  16. 16. 2つの型が同じ場合 呼び出し可能な関数 class Tag[A, B] ()implicit def equalAllowed[A] = new Tag[A, A]def func[A, B](implicit ev: Tag[A, B]) = "OKscala> func[Int, Int] // → OKscala> func[Int, String] // → コンパイルエラー
  17. 17. インスタンス生成の無駄を省く class Tag[A, B] ()val singletonTag = new Tag[Any, Any]implicit def equalAllowed[A]: Tag[A, A] = singletonTag.asInstanceOf[Tag[A, A]]def func[A, B](implicit ev: Tag[A, B]) = "OK" •  implicitで呼び出される度にインスタンス⽣生成するのは無駄 •  implicitで渡される値は使われないので、型さえあえば、中 ⾝身はなんでもいい •  nullでもOK
  18. 18. 2つの型パラメータをとる型の シンタックスシュガー scala> class Op[A, B] ()scala> val x: Op[Int, String] = new Opx: Op[Int,String] = Op@ab612f8scala> val x: Int Op String = new Opx: Op[Int,String] = Op@165e6c89•  中置記法の⽅方が型の演算⼦子っぽく⾒見見える•  クラス名に記号も使える。記号の⽅方がより演算⼦子っぽい
  19. 19. Scala 2.10-M7の実装 sealed abstract class =:=[From, To]                  extends (From => To) with Serializableprivate[this] final val singleton_=:= = new =:=[Any,Any] { def apply(x: Any): Any = x }object =:= { implicit def tpEquals[A]: A =:= A = singleton_=:=.asInstanceOf[A =:= A]}•  From => To を継承しているため、型を変換する関数とし て使えるdef func[A](x1: A)(implicit w: A =:= B ) = { val x2 = w(x1) // x2はB型として使える ...}•  Serializableを継承している利⽤用は不明
  20. 20. generalized type constraints scala> implicitly[Int =:= Int]res7: =:=[Int,Int] = <function1>scala> implicitly[Int =:= String]<console>:9: error: Cannot prove that Int =:= String. implicitly[Int =:= String] ^ •  異なる型を渡すとコンパイルエラー
  21. 21. Prove? これは、あの証明ですか? ← あの証明です
  22. 22. 証明は身近な存在 •  カリー・ハワード同型対応 •  型 ⇆ 命題 •  プログラム ⇆ 証明•  プログラムに型が付く ⇆ 命題が証明された•  「A =:= B」型について、それを満たすimplicitをコ ンパイラが⾒見見つけて、型が付けば、「AがBであるこ と」が証明されている
  23. 23. 定理証明支援器 Coq •  型の表現⼒力力がすごい 例:迷路の経路が最短経路である型 http://d.hatena.ne.jp/yoshihiro503/20100119•  そんな型を満たすようなプログラムを書くのは⼤大 変 •  Coqは⼈人にやさしい形で⽀支援してくれる
  24. 24. 一方、Scalaは •  型レベルλ計算 http://apocalisp.wordpress.com/ 2010/06/08/type-level-programming-in- scala/•  型レベル命題論理 http://www.chuusai.com/2011/06/09/scala- union-types-curry-howard/•  ⼀一応、がんばれば、、、
  25. 25. まとめ •  Phantom Type(幽霊型)はコンパイル時に暗躍•  使いこなすには型レベルでいろいろやりたくなる•  Scalaでは、implicit parameterを利⽤用して、コ ンパイル時にいろいろできる•  Coqすごい
  26. 26. もっと型の力を知りたい人は •  ProofCafe(毎⽉月第4⼟土曜) •  名古屋でプログラムの証明について勉強する勉強会で す。現在はソフトウェアの基礎というドキュメントを 読んでいます。コーヒーを飲みながら楽しく証明しま しょう。 http://proofcafe.org/wiki/•  TAPL-nagoya (毎⽉月第3⼟土曜) •  ScalaやF#やSML#などの静的型付け⾔言語の基礎に なっている型理論についての勉強会です。英語ですが、 説明が丁寧で例が豊富な TAPL をみんなで読みます。 •  http://proofcafe.org/tapl/
  27. 27. その他
  28. 28. <%< はなくなった (2.10から) •  「A => B」でOKscala> implicitly[ Int <%< Long ]<console>:12: error: not found: type >%> implicitly[ Int <%< Long ] ^scala> implicitly[ Int => Long ]res5: Int => Long = <function1>
  29. 29. <%<は、なぜなくなったのか •  SI-2781•  type inference constraints should be carried along during search for chained implicits•  https://issues.scala-lang.org/browse/ SI-2781
  30. 30. 演習問題 1 •  9ページの例をコンパイルエラーを出すようにするscala> Dinner.start.cookSalad.cookCake.serve()<console>:8: error: Cannot prove that Dinner.Empty =:=Dinner.Ready. Dinner.start.cookSalad.cookCake.serve() ^scala> Dinner.start.cookSalad.cookCake.cookSalad<console>:8: error: Cannot prove that Dinner.Ready =:=Dinner.Empty. Dinner.start.cookSalad.cookCake.cookSalad ^scala> Dinner.start.cookSalad.cookCake.cookSteak.serve()Now you can eat!
  31. 31. 演習問題 2 •  AがBの⼦子クラスであるかをチェックする型を考えてみるscala> trait Ascala> trait B with Ascala> implicitly[ B <:< A ]res9: <:<[B,A] = <function1>scala> implicitly[ A <:< B ]<console>:14: error: Cannot prove that A <:< B. implicitly[ A <:< B ]
  32. 32. 参考文献 •  Type classes as objects and implicitsBruno C.d.S. Oliveira, Adriaan Moors, Martin Odersky OOPSLA ʻ‘10 Proceedings of the ACM international conference on Object oriented programming systems languages and applications, 2010 この論⽂文の「6.6 Encoding generalized constraints」に>:>の話がある(少しだけど)

×