2013-04-17 8 views
12
は、私は、これはコードが少し愚かであることを知っている

、誰かが、なぜこれがisList [42]戻りTrueisList2 [42]に対しプリントFalse、そしてどのようにこれを防ぐために説明することができますか?私はより多くの無名のGHC型拡張のいくつかのより良い理解を取得したいのですが、私はこれを把握することは興味深い例だろうと思いました。Haskellの重複/インコヒーレントインスタンス

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE OverlappingInstances #-} 
{-# LANGUAGE IncoherentInstances #-} 

class IsList a where 
    isList :: a -> Bool 

instance IsList a where 
    isList x = False 

instance IsList [a] where 
    isList x = True 

isList2 = isList 

main = 
    print (isList 42) >> 
    print (isList2 42) >> 
    print (isList [42]) >> 
    print (isList2 [42]) 
+5

あなたのプログラムはインコヒーレントなので、本当に、あなたは何を期待しましたか? –

+1

私は出力が 'True True'ではなくTrue Trueであることを期待しました。なぜあなたは出力が真の偽であると思いますか?インコヒーレントなプログラムがTrue Trueを返さないのはなぜですか?あなたは出力の理由がないと言っていますが、その結果は未定義です。本当にTrue Trueを返すことがありますか? – Clinton

+1

可能性のある複製の[IncoherentInstancesはどのように動作しますか?](http://stackoverflow.com/questions/8371499/how-does-incoherentinstances-work) –

答えて

15

これは本当に簡単です。

∀x. x ⊢ :t isList2 
isList2 :: a -> Bool 

これは(統一を経て、でもそれはできるけれども)[a]インスタンスと一致していないが、それはすぐにaインスタンスと一致しません:のはisList2の種類が何であるかをGHCiのを聞いてみよう。したがって、GHCはaインスタンスを選択し、そうisList2戻るFalse

この動作は、正確に何IncoherentInstances手段です。実際には、これはむしろ素晴らしいデモンストレーションです。あなたは、単にIncoherentInstancesを無効にした場合


は陽気に、我々は全く逆の効果を取得し、GHCiのは、今、この言葉:

∀x. x ⊢ :t isList2 
isList2 :: [Integer] -> Bool 

isList2は、関数構文を使用して定義されていない結合のトップレベルであるためですしたがって恐ろしい一形態制限に従う。したがって、実際に使用されるインスタンスに特化します。

NoMonomorphismRestrictionを追加するだけでなく、IncoherentInstancesを無効にすることを、我々は代わりにこれを取得:

∀x. x ⊢ :t isList2 
isList2 :: IsList a => a -> Bool 
∀x. x ⊢ isList2 'a' 
False 
∀x. x ⊢ isList2 "a" 
True 
∀x. x ⊢ isList2 undefined 

<interactive>:19:1: 
    Overlapping instances for IsList a0 arising from a use of `isList2' 

選択肢があいまいな場合は、使用や苦情に基づいて選ばれたインスタンスと予想重複行動であり、これは。


質問の編集に関して、私はタイプ注釈なしで希望の結果が可能であるとは思わない。

最初のオプションは、isList2にタイプシグニチャを指定することです。これにより、IncoherentInstancesがインスタンスをあまりにも早く選択できなくなります。

isList2 :: (IsList a) => a -> Bool 
isList2 = isList 

はおそらく、引数に適用されることなく、どこか他のisListは(たとえ間接的に)記載されて同じことを行う必要があります。

2番目のオプションは、数値リテラルを明確にし、IncoherentInstancesを無効にすることです。

この場合
main = 
    print (isList (42 :: Integer)) >> 
    print (isList2 (42 :: Integer)) >> 
    print (isList [42]) >> 
    print (isList2 [42]) 

、そうOverlappingInstancesはそのことをし、最も特定のインスタンスを選択するのに十分な情報があります。

+0

質問を編集しました。上記をコンパイルして 'False False True True'を返す方法を説明できますか? – Clinton

+0

@Clinton:そうするための素晴らしい方法はないと思います。私の答えに編集を参照してください。 –

+0

@Clinton私はもう少し先に進み、それを行うには良い方法ではないと言います。 GHCは、型抜きに関してはオープンワールドの前提に従わなければならず、ある種の型クラスの欠如に基づいて決して決断を下してはならない。 'isList 42'が' False'を返すためには、実際にはその仮定に違反する必要があります。そのようなtypeclassの存在が 'False'から' 'あなたが選んだインスタンスに依存する」という答えを変えるので、インスタンスのNum [a]は存在しません。 'Bool'型の有効な値です。 – semicolon

2

codeIncoherentInstancesを必要とせずにトリックん:あなたは黙って非常に簡単にコンテキストに応じて異なるオーバーロードを呼び出すことができるよう

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE OverlappingInstances #-} 

class IsList a where 
    isList :: a -> Bool 

instance IsList a where 
    isList x = False 

instance IsList [a] where 
    isList x = True 

isList2 :: (IsList a) => a -> Bool 
isList2 = isList 

main = do 
    print (isList (42 :: Int)) 
    print (isList [42 :: Int]) 
    print (isList2 (42 :: Int)) 
    print (isList2 [42 :: Int]) 

私はIncoherentInstancesを使用していないお勧めしますが、多くの問題を引き起こしているようです。