存在型を学ぼう

@lyrical_logical

rpscala 164 回

Existential Quantifier

存在量化子

発端

  • Kenji Yoshida (twitter id: xuwei_k)
    • ​scalaz comitter
    • よくキャッチアップだけして解説任せるマン
      • アンテナ性能は非常に優秀
    • 任されてみました
  • purescript
    • Haskell like な altjs
  • Exists
    • ​purescript で存在量化子を表現するデータ型
  • 存在量化子
    • ​述語論理とかで使う量化子
    • 記号としては ∃
    • ある○○について~
      • ​ある○○の存在を仮定して~
    • 双対として全称量化子がある
      • ​こっちの方が popular
  • 全称量化子
    • ​述語論理にとかで使う量化子
      • ​popular な方
    • 記号としては ∀
    • 任意の○○について~

プログラミングとの関係は?

  • Curry-Howard Isomorphism
    • カリー・ハワード同型
    • カリー・ハワード対応とも
      • ​Curry-Howard Correspondence

Proofs as Programs

プログラムとしての証明

Propositions as Types

型としての命題

  • Curry-Howard Isomorphism
    • カリー・ハワード同型
    • カリー・ハワード対応とも
      • ​Curry-Howard Correspondence
    • ​Proofs = Programs
      • ​証明もプログラムも似たようなもの
    • Propositions = Types
      • ​命題も型も似たようなもの
  • Proofs = Programs
    • ​証明もプログラムも似たようなもの
  • Propositions = Types
    • 命題も型も似たようなもの
  • ​証明するには何らかの論理が必要
    • ​プログラミング言語のようなもの
  • 述語論理では量化子が使われる
    • ​対応する言語機能(や表現)があるはず
  • 全称量化子
    • 任意の○○について~
  • ​プログラムでは…?
    • ​Generics 等が相当
trait Forall[A] { ... }
// 任意の A について ...
  • 存在量化子
    • ​ある○○について~
  • ​プログラムでは…?
    • ​Existential Types が相当(名前通り)
      • ​SLS 3.2.10 参照
      • SLS = Scala Language Specification
import scala.language.existentials
class Ex(a: A forSome { type A })
// ある A について ...
// ある A の存在を仮定して ...

言語機能にある

これでおしまい?

  • forSome
    • ​Java の Wildcards 相当
      • ​singleton types があるのでちょっとだけ強い
    • 実は大した表現力はない
// これらはできる
T forSome { type T }
T forSome { type T <: Upper }
T forSome { type T >: Lower }
T forSome { type T >: Lower <: Upper }
// こういうのはだめ
T forSome { type T : ContextBound }
  • つまり…
    • ​以下はできる
      • ​ある型を継承した型について~
      • ある型を親クラスに持つ型について~
    • 以下は *** できない ***
      • ある性質を持つ型について~
        • ​「ある性質」は継承関係を除く
      • ​より practical には
        • ​ある Monoid な型について~

存在量化としては

限定的でしかない!

  • 一方 Haskell は?
    • ​できます​(GHC 拡張で)
    • 値コンストラクタに対する全称量化で表現
data Nanika = forall a. Nanika a
-- Nanika :: forall a. a -> Nanika
-- 擬似コードですが、以下に相当
-- Nanika :: (exists a. a) -> Nanika
  • 値コンストラクタに対する全称量化で表現
    • ​Scala でもできるんじゃ?
      • ​GADTs あると、できてしまうんだなあ
sealed trait Foo
case class Bar[T](t: T) extends Foo
  • しかし不便
    • ​元々のモチベーションである問題発生
      • ​パターンマッチ時に型がわからない
foo match {
  // t の型がわからず Any になる! 
  case Bar(t) => ...
}

話を戻す

  • purescript の Exists とは
    • exists a. f a を表現する
      • ある a についての f a を表現
      • ​exists a で導入される a には制限がない
        • ​なんの性質も持たない任意の型
      • ​型パラメタとしてでてきちゃう
        • ​が、見せるほどのものではないことがある
          • ​これを「隠す」ためのもの
  • ​型パラメタとしてでてきちゃう
    • ​が、見せるほどのものではないことがある
      • これを「隠す」ためのもの
    • 具体例
      • ​A を提供する Stream
      • Impl は実装の詳細
        • 別に​見せたくない
trait Stream[A, Impl] { ... }
  • ​abstract type member​s​ を使う
    • 何の型なのかは分からない
      • ​目的に合致している
trait Stream[A] {
  type Impl
  ...
}
  • ​abstract type member​s​ を使う
    • Upper bounds, Lower Bounds 使える
    • Context Bounds も擬似的に表現可能
      • ​メンバとして要求
trait Stream[A] {
  type Impl <: Any >: Nothing
  def ev: Monoid[Impl]
  ...
}
  • purescipt の Exists を Scala で
    • ​Rank2-polymorphism がなくてちょっとダサい
      • ​オブジェクトとメソッドで擬似的に表現
sealed trait Exists[F[_]] { type A }
object Exists {
  def apply[F[_], A](fa: F[A]): Exists[F] = ExistsImpl(fa)
  def run[F[_], R](runner: Rank2[F, R], ex: Exists[F]): R =
    ex match { case ExistsImpl(fa) => runner.apply(fa) }
  type Rank2[F[_], R] = { def apply[A](fa: F[A]): R }
  case class ExistsImpl[F[_], T](fa: F[T]) extends Exists[F] {
    type A = T
  }
}
  • Exists を使った例
    • ​StreamF から Stream を構築できるぞい
type StreamF[A, S] = S => (S, A)
type Stream[A] = Exists[({ type App[S] = StreamF[A, S] })#App]
  • まとめ
    • ​存在型=述語論理における存在量化子
    • ​Scala には言語機能として予め備わっている
      • ​表現力があまり高くない
        • ​他の言語機能を使って実現しよう
    • ​​abstract type members
      • 存在型を表現可能
      • ​existential types より表現力豊か
        • ​Monoid であることを要求したり…

forSome とか

誰も使わないよね

おわり

Made with Slides.com