このコードを考えてみましょう:
{-# LANGUAGE GADTs #-}
data P t where
PA :: P Int
PB :: P Double
PC :: P Char
isA PA = True
isA _ = False
コンパイルして正常に動作します。次のコードを考えてみましょう:
{-# LANGUAGE GADTs #-}
data P t where
PA :: P Int
PB :: P Double
PC :: P Char
isA PA = True
isA PB = False
isA PC = False
コンパイルに失敗します:
Main.hs:8:10: error:
• Couldn't match expected type ‘p’ with actual type ‘Bool’
‘p’ is untouchable
inside the constraints: t ~ Int
bound by a pattern with constructor: PA :: P Int,
in an equation for ‘isA’
at Main.hs:8:5-6
‘p’ is a rigid type variable bound by
the inferred type of isA :: P t -> p
at Main.hs:(8,1)-(10,14)
Possible fix: add a type signature for ‘isA’
• In the expression: True
In an equation for ‘isA’: isA PA = True
• Relevant bindings include isA :: P t -> p (bound at Main.hs:8:1)
|
8 | isA PA = True
| ^^^^
Main.hs:9:10: error:
• Couldn't match expected type ‘p’ with actual type ‘Bool’
‘p’ is untouchable
inside the constraints: t ~ Double
bound by a pattern with constructor: PB :: P Double,
in an equation for ‘isA’
at Main.hs:9:5-6
‘p’ is a rigid type variable bound by
the inferred type of isA :: P t -> p
at Main.hs:(8,1)-(10,14)
Possible fix: add a type signature for ‘isA’
• In the expression: False
In an equation for ‘isA’: isA PB = False
• Relevant bindings include isA :: P t -> p (bound at Main.hs:8:1)
|
9 | isA PB = False
| ^^^^^
Main.hs:10:10: error:
• Couldn't match expected type ‘p’ with actual type ‘Bool’
‘p’ is untouchable
inside the constraints: t ~ Char
bound by a pattern with constructor: PC :: P Char,
in an equation for ‘isA’
at Main.hs:10:5-6
‘p’ is a rigid type variable bound by
the inferred type of isA :: P t -> p
at Main.hs:(8,1)-(10,14)
Possible fix: add a type signature for ‘isA’
• In the expression: False
In an equation for ‘isA’: isA PC = False
• Relevant bindings include isA :: P t -> p (bound at Main.hs:8:1)
|
10 | isA PC = False
| ^^^^^
どうして?何が起きてる?
編集:型シグネチャを追加するisA :: P t -> Bool
はそれを機能させるので、私の質問は次のようになります。最初のケースでは機能するので、なぜ2番目のケースでは型推論が機能しないのですか?
免責事項:コメントに収まらないため、これを回答として記述します。しかし、私は間違っているかもしれません
この動作は、GADTs
でパターンが一致する場合に予想されます。 GHC
まで ユーザーマニュアル :
型の改良は、ユーザー指定の型注釈に基づいてのみ実行されます。したがって、evalに型シグネチャが指定されていない場合、型の調整は行われず、多くの不明瞭なエラーメッセージが表示されます
また、ユーザーマニュアルから:
GADTから取得したデータコンストラクターとのパターンマッチングを行う場合(ケース式など)、次のルールが適用されます。
Scrutineeのタイプは厳格でなければなりません。
ケース式全体の型は厳密でなければなりません。
代替案のいずれかで言及されている自由変数のタイプは厳密でなければなりません。
注:タイプ変数は固定iffで、ユーザーが指定します。
これまで、GADT
に対してパターンマッチングを行う場合、型のシグネチャを提供する必要があります(理由は、GADTs
では型の推論が難しいためです)。したがって、明らかにisA
の最初の定義はコンパイルに失敗するはずですが、 paper でGADTs
の型推論が説明されています(セクション6.4):
セクション4.3で、PCON-Rでは、最も一般的なもの以外のユニファイアを使用することは不適切であると述べました。しかし、洗練はまったく統一者でなければなりませんか?たとえば、case式は絞り込みを行うことができますが、この関数をタイプチェックするために絞り込みは必要ありません:
f :: Term a -> Int
f (Lit i) = i
f _ = 0
上記の例はまさにあなたのケースです!。論文では、これはpre-unifierと呼ばれ、これがどのように機能するかについては非常に技術的な説明がありますが、私が理解できる限り、執筆時に:
data P t where
PA :: P Int
PB :: P Double
PC :: P Char
isA PA = True
isA PB = False
isA PC = False
コンパイラーはisA :: P t -> p
を推定することから始め、タイプ変数は固定されていない(つまり、ユーザー指定ではない)ため、続行を拒否します。
一方、書くとき:
data P t where
PA :: P Int
PB :: P Double
PC :: P Char
isA PA = True
isA _ = False
コンパイラーは、型の推論は、戻り値の型としてBool
を推測するよりも一般的ではないと推測できるため、isA :: P t -> Bool
を安全に推測できます。
おそらくこれは私にはあなたにとって不明瞭に思えますが、確かにあなたが求める2つのケースは実際に文書化されているので、おそらくこれはGHC
開発者にとって望ましい動作であり、奇妙なバグではありません。