読者です 読者をやめる 読者になる 読者になる

Haskellの太いほうの矢印について

Haskellには頻出する矢印が2つある(->=>)。 なかでも=>(型制約導入子?)がくせものである。

  • くせものポイント1:数学の「ならば」とは(一見)違う意味を持つ。
  • くせものポイント2:もちろん日本語の「ならば」とも違う意味を持つ。
  • くせものポイント3:Haskellの中でも型クラス宣言と型インスタンス宣言で意味が違う。
  • くせものポイント4:関数宣言の引数側の制約となるか返り値側の制約となるかでも意味が違う。

というわけで、=>の使いかたを今一度整理しよう。

1. 型クラス宣言の=>

class Functor a => Applicative a where ..

意味:Applicative aならばFunctor a(逆方向)である。

2. 型インスタンス宣言の=>

instance (Foldable f, Monoid m) => Summable (f m) where ..

意味:Foldable fかつMonoid mならばSummable (f m)(順方向)である。

3. 関数宣言の=>

引数側に制約がある場合:

shinyArgFunc :: Sparkling a => a -> Int

意味:shinyArgFuncは「Sparklingインスタンス」からIntへの写像である。

返り値側に制約がある場合:

shinyReturnFunc :: Sparkling a => Int -> a

意味:shinyReturnFuncIntから「Sparklingインスタンス」への写像である。

両方に同じ型変数がある場合:

truelyShinyFunc :: Sparkling a => a -> a

意味:truelyShinyFuncは「Sparklingインスタンス」から「Sparklingインスタンス」への写像である。つまり、引数側の規則が優先される。

4. 無制約の型制約について

型制約を入れずに型変数を登場させることは、 無制約の型クラスを導入することと等価である。 従って、上のルール1-3に注意すると、無制約の型変数xから自明な実装が導かれる:

-- とあるクラスの..
class Trivial a where
  trivialThing :: a

-- 自明な無制約インスタンス
instance Trivial x where
  trivialThing = undefined

-- 自明な無制約値
trivialValue :: x
trivialValue = undefined

全ての型に共通する要素がundefinedしか無いことから、 これらの実装はundefined以外にありえない。 このへん、インスタンス宣言における型制約導入と関数の返り値における型制約導入の類似性が滲み出ている。

5. まとめ

  • 型クラス宣言と型インスタンス宣言における型制約は集合論的には意味が逆*1
  • 関数宣言における型制約も場所によって意味が逆転する。
  • 無制約の型制約による対比から、型クラス↔引数、型インスタンス↔返り値の対応が取れる。

*1:この場合の型制約導入子はインスタンスの実装順を表すとも取れる。そう考えると意味は一緒。