web-dev-qa-db-ja.com

すべてのケースを列挙してもワイルドカード一致が機能しないのはなぜですか?

このコードを考えてみましょう:

{-# 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の最初の定義はコンパイルに失敗するはずですが、 paperGADTsの型推論が説明されています(セクション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開発者にとって望ましい動作であり、奇妙なバグではありません。

1
lsmor