2009-12-03 [長年日記]

[Haskell] インスタンス宣言と拡張機能

たとえばこんなクラスがあるとして、

class ToString a where
  toString :: a -> String

以下の型にインスタンスを宣言することを考えます。

newtype Wrap a = Wrap a

普通のインスタンス宣言は、たとえばこんな感じ。

instance Show a => ToString (Wrap a) where
  toString (Wrap x) = "Normal:" ++ show x

たとえば、ghciで以下のように試すことができます。

> toString (Wrap 1)
"Normal:1"

FlexibleInstances拡張を使うと、特定の型に対するインスタンスを宣言することができます。先のインスタンス宣言をコメントアウトしてから以下のインスタンス宣言をしてみます。

instance ToString (Wrap Int) where
  toString (Wrap x) = "Int:" ++ show x

使ってみます。

> toString (Wrap (1 :: Int))
"Int:1"

ここでコメントアウトしてあった方のインスタンス宣言も有効にすると、インスタンス宣言がオーバーラップしているというエラーになります。OverlappingInstances拡張を有効にすると、より具体的な宣言が使われるようになります。

> toString (Wrap (1 :: Int))
"Int:1"

しかし、最初に試した、toString (Wrap 1)を試すとエラーになります。これは、1の型がNum a => aなので、先のインスタンス宣言にマッチすることは確実ですが、二番目のインスタンス宣言にマッチするかどうかは具体的な型がIntかどうかわからないので判断できないためです。IncoherentInstances拡張を有効にすると、二番目のインスタンス宣言は無視して最初のインスタンス宣言を使うようになります。

> toString (Wrap 1)
"Normal:1"

二番目のインスタンス宣言が無視されていることは、以下のような関数を書いてみるとわかります。

f :: Num a => Wrap a -> String
f = toString

そして、以下のように試してみます。

> f (Wrap (1 :: Int))
"Normal:1"
> toString (Wrap (1 :: Int))
"Int:1"

Intの1を渡しているのに、fを経由している場合には、二番目のインスタンスが無視されて最初のインスタンスが使われているのがわかります。

[]

トップ «前の日記(2009-11-30) 最新