Phantom Type in Scala

4,687 views

Published on

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

Published in: Technology
1 Comment
7 Likes
Statistics
Notes
  • 「Type Classes as Objects and Implicits」という論文の「6.6 Encoding generalized constraints」にFrom => To を継承している理由が記載されていました。19ページに追記しました。論文はこちら → http://ropas.snu.ac.kr/~bruno/papers/TypeClasses.pdf
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
4,687
On SlideShare
0
From Embeds
0
Number of Embeds
92
Actions
Shares
0
Downloads
10
Comments
1
Likes
7
Embeds 0
No embeds

No notes for slide

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」に>:>の話がある(少しだけど)

×