関数の型ではなく引数の型に制約を置くとどうなりますか?


8

関数の型ではなく、関数の引数の型に制約を設定します。
これは構文エラーになるか、関数のタイプに情報が追加されると思いました。
しかし、制約は完全に無視されているようです。

{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE RankNTypes #-}

test :: a -> String
test (n :: (Num a, Ord a) => a) =
    if n > 10 then "Hello"
    else "World"

main = print "Hello World"

これにより、次のタイプのエラーが発生します。

Test3.hs:6:8: error:
     No instance for (Num a) arising from a use of n
      Possible fix:
        add (Num a) to the context of
          the type signature for:
            test :: forall a. a -> String
     In the first argument of ‘(>)’, namely n
      In the expression: n > 10
      In the expression: if n > 10 then "Hello" else "World"
  |
6 |     if n > 10 then "Hello"
  |        ^

Test3.hs:6:8: error:
     No instance for (Ord a) arising from a use of ‘>’
      Possible fix:
        add (Ord a) to the context of
          the type signature for:
            test :: forall a. a -> String
     In the expression: n > 10
      In the expression: if n > 10 then "Hello" else "World"
      In an equation for test’:
          test (n :: (Num a, Ord a) => a)
            = if n > 10 then "Hello" else "World"
  |
6 |     if n > 10 then "Hello"
  |  

引数の型に制約を置くことは実際には何をしますか?

編集:

なぜこれにRankNTypes拡張が必要なのですか?制約
を削除する場合は必要ありません(Num a, Ord a) =>


コメントは詳細な議論のためのものではありません。この会話はチャットに移動しました
Samuel Liew

回答:


7

これは、ここ説明されているように、制約の包摂と相互作用する、多態的な包摂のエキゾチックなインスタンスです。

タイプの場合はa包摂b、その後、exp :: a暗示exp :: b表面言語で。包摂の特定の例は、それがf :: forall a. a -> a意味することf :: Int -> Intです。また、すべての制約をn :: Int暗示n :: c => Intしていcます。

ただし、コア言語では包含はまったくありません。表面言語の包摂のすべての場合は、明示的なラムダとアプリケーションに翻訳する必要があります。また、c => a単純になりc -> a、制約された関数の使用はf :: c => a、いくつかのの単純な関数適用に変換されますinst :: c。したがって、f :: forall a. a -> aとなりf @Int :: Int -> Int、および n :: Intなり\_ -> n :: c -> Int

まれに使用されるケースは、関数の反変包摂規則です。以下は有効なコードです。

f :: (Int -> Int) -> Bool
f _ = True

g :: (forall a. a -> a) -> Bool
g = f

これは

f :: (Int -> Int) -> Bool
f = \_ -> True

g :: (forall a. a -> a) -> Bool
g = \x -> f (x @Int)

制約の包含も同様に機能します。

f :: forall a. (Eq a => a) -> Bool
f _ = True

g :: forall a . a -> Bool
g = f

に翻訳されます

f :: forall a. (Eq a -> a) -> Bool
f = \_ -> True

g :: forall a . a -> Bool
g = \x -> f (\_ -> x)

元の質問に近づく

f (x :: Eq a => a) = True

一番上の定義として、その推定型はforall a. (Eq a => a) -> Boolです。ただし、f推論された型に包含される任意の型注釈を使用できます。したがって、次のようになります。

f :: forall a. a -> Bool
f (x :: Eq a => a) = True

そしてGHCはまだ幸せです。元のコード

test :: a -> String
test (n :: (Num a, Ord a) => a) =
    if n > 10 then "Hello"
    else "World"

次のバージョンのわずかに明確なものと同等です。

test :: forall a. a -> String
test (n :: (Num a, Ord a) => a) =
    if n > 10 then "Hello"
    else "World"

あなたが得るタイプの誤差があるため、単純でn実際には2つの引数を持つ関数であり、1は種類ありNum a、その他にOrd a、そしてこれらの引数の両方を含むレコードあるNumOrd方法。ただし、定義のスコープにはそのようなインスタンスがないためn、数値として使用することはできません。翻訳はに変換さn > 10れます(>) inst (n inst) (10 inst)inst :: Num a、にはありませinstん。そのため、翻訳できません。

したがって、testコードの本文は引き続きでチェックされn :: (Num a, Ord a) => a)ます。ただし、を使用せずに単に「Hello」を返す場合はn、前のfケースと同様に、forall a. a -> String注釈型を包含する推定型を取得します。包摂は、その後のすべての発生を置き換えることによって翻訳出力で実現されるnの体内にtest有します\_ -> n。ただしn、本文では発生しないため、翻訳はここでは何も行いません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.