、Haskellのプログラムに推論さ種類は、その意味を変更しない一致しますが、それは実際には一般的なケースでは保証されていない、明示的な型シグネチャを追加します。 (私はそれはしかし、Haskell98のトップレベルの定義のための保証であると信じています。)
最終的に、あなたの問題はHaskell98内のローカルな定義で発生することができますタイプの変数のスコープの問題の一種と大差ありません:
import Data.List
sortImage :: Ord b => (a -> b) -> [a] -> [a]
sortImage f = sortBy cmp
where cmp x y = compare (f x) (f y)
cmp
の推定タイプは、実質的に(Ord b) => a -> a -> Ordering
です。あなたが書くことができ、その場合にはScopedTypeVariables
を、使用しない限り、あなたが戻って、外側の署名(特にf
の種類)にa
とb
を結ぶことができないので、あなたは、しかし、この署名が明示することはできません。
sortImage :: forall a b . Ord b => (a -> b) -> [a] -> [a]
sortImage f = sortBy cmp
where cmp :: a -> a -> Ordering
cmp x y = compare (f x) (f y)
あなたが発見したように、この種の変数スコープの問題は、少なくともAllowAmbiguousTypes
を有効にしてトップレベルの定義でも発生する可能性があります。
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
class D a b
instance D Bool b
instance D Int b
strange :: D a b => a -> a
strange = undefined
-- stranger :: (D a b1, D a b) => a -> a
stranger x = strange (strange x)
私はコメントとしてstranger
の推論されたタイプを示している:ここで
は、私がAllowAmbiguousTypes
延長上GHCのドキュメントから適応、同じ問題であると考えているものを示してシンプルな例です。あなたはそれが明示的に作るしようとすると、エラーになります:
• Could not deduce (D a b0) arising from a use of ‘strange’ from the context: (D a b2, D a b)
問題は、GHCがstranger
が外側strange :: D a b1 => a -> a
ためD a b1
を満たす任意のa
上と呼ばれ、またためD a b
を満たすことができることを推測することができるということです内側strange :: D a b => a -> a
。 a
関係限りしかし
、あなたは、明示的にこのタイプの署名を作るためにstranger
の明示的な署名でb1
とb
変数間のリンクをしようとstrange
通話の種類との関係が失われた場合、 b
の仮説ではcmp
の署名であり、sortImage
の署名のa
とb
の署名は失われます。制約はさておき、strange
の種類がちょうどa -> a
で、直接b
を参照していない、ので、一人でScopedTypeVariables
を使用して
は、ここで問題を解決するには十分ではありません。
stranger :: forall a b1 b2 . (D a b1, D a b2) => a -> a
stranger x = (strange :: a -> a) ((strange :: a -> a) x)
をしかし、あなたはstrange
通話の種類にb1
とb2
を結ぶことができません:だから、あなたが書くことができます。あなたはそれを行うにはTypeApplications
が必要:
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
class D a b
instance D Bool b
strange :: forall a b . D a b => a -> a
strange = id
stranger :: forall a b1 b2 . (D a b1, D a b2) => a -> a
stranger x = (strange @a @b1) (strange @a @b2 x)
し、それは大丈夫チェックを入力して、あなたも呼び出すことができます。
> stranger False
False
を(やや驚くべきことである)任意の型注釈なし。あなたは、インスタンスを持っていた場合:
instance D Int Double
しかし、その後、あなたはInt
の上stranger
に使用して明示的に指定する必要があるでしょう:
> stranger @_ @Double @Double (1 :: Int)
'A'は何ですか?あいまいだと思われる。 – leftaroundabout
これはNumインスタンスのものです。私は可読性のためにいくつかの制約を取り除いたが、なぜ宣言が他の制約なしではあまり意味がないのか分かる。 – RichardW
'a'は何らかの形で' => 'の後ろにリンクされていなければなりません。(あるいは少なくともコード自体が拡張されていれば)GHCは常にそのような使い方を理解できません。 'a' - 代わりに別の型変数' a0'を引き続き推論し、 'a0'に必要な制約を導き出すことができないと不平を言う。それは冒頭で困惑するかもしれませんが、それはどのように動作しなければなりません。結局、 'f'は' a'以外の型で 'f'を呼び出すことができます。 – chi