Subscribed unsubscribe Subscribe Subscribe

Re: Haskell でダックタイピング 、色々な型を混ぜて配列を作る。

Haskell

この forall a. という表記は冗長な気がするんだけど何で要るんだろう。。。

Haskell でダックタイピング 、色々な型を混ぜて配列を作る。 - 言語ゲーム

existential typeのforallキーワードが必要なのには理由があります。元の定義Valをよくみてみると、

data Val = forall a. (Show a) => V a

左側(LSH; Left Hand Side)に型変数aが出てきません。何故このような定義になっているかは、普通の型を定義してみればよくわかります*1

data Show a => Val a = V a

この型Val aはヘテロなリストを構築するのには使えません。

Prelude> :t [V "1", V 2, V 3.4]

<interactive>:1:15:
    No instance for (Fractional [Char])
      arising from the literal `3.4' at <interactive>:1:15-17
    Possible fix: add an instance declaration for (Fractional [Char])
    In the first argument of `V', namely `3.4'
    In the expression: V 3.4
    In the expression: [V "1", V 2, V 3.4]

理由は各々の要素の型を考えれば当然です。

  • V "1" :: Val String
  • V 2 :: Num t => Val t
  • V 3.4 :: Fractional t => Val t

元の値をデータ構成子Vでラップしても、できあがったVal aの型がそれぞれ異なる型になってしまうからです。言い換えるとVal aのaを消してしまえば、ヘテロなリストを作れるようになります。このために使うのがexistential typesです。

existential typesについてはHaskell/Existentially quantified types - Wikibooks, open books for an open world参考訳)に素晴らしい解説があります。

Existential types, or 'existentials' for short, are a way of 'squashing' a group of types into one, single type.

Haskell/Existentially quantified types - Wikibooks, open books for an open world

複数の型を一つの型に押しつぶす(squashing)ということです。これはまさに欲しかったものです。先ほどVal StringやNum t => Val tを一つのリストに詰めようとして失敗しましたが、これを一つの型Valに押しつぶすことができれば、難なくリストに詰められるようになる、というわけです。

ポイントをまとめると、

  • データ型Valの定義のLHSに型変数が現れないこと
  • そのためにはRHSに出てくる変数がforallキーワードで全称限量されている必要がある
  • 2つをあわせるとexistential quantifiedな型が作れる

ということです。

*1:data宣言中に型の制約を書くのは、一般的には悪いスタイルです。cf. http://www.haskell.org/haskellwiki/Data_declaration_with_constraint