2014-01-14 14 views
5

これはthis questionへのフォローアップの質問ですHaskellの多型の機能評価エラー

Couldn't match type `Integer' with `Double' 
Expected type: Either Integer Double 
Actual type: Either Integer a 
In the expression: Right $ f d 
In a case alternative: Right d -> Right $ f d 

を、問題がによって解決されますRankNTypesを使用して:

(forall a. Num a => a -> a) 

しかし、答えは何も言わなかった。私は知りたいです:

  • このエラーの原因は何ですか? 最終的な結果は大文字小文字ブランチの1つに過ぎませんが、fは同時に2つの型には入力されません。fの型は、f :: Num a => (a -> a)、Integer - > IntegerまたはDouble - > Doubleなぜ、これがエラーを引き起こすのかを誰かが詳しく調べることができますか?

  • エラーを解決する他の方法はありますか? RankNTypesがエラーを解決する理由これは先日私が得たMonomorphism Restrictionエラーのようなものですが、これを修正するのに役立つものではなく、明示的な型の注釈も機能しません。

+1

ちょうどそれがtypecheckを想像してください。 'foo(+ 1.5)(Left 5)'と呼ぶことができますか?そうでない場合は、どうしてですか?はいの場合、結果はどうなりますか? –

+1

型の問題とは別に、 'foo f =(左.f)(右.f)'と書くことができます。 –

答えて

6

元の定義では、aが一般的すぎます。 考えてみます。この時点で

foo :: Num a => (a -> a) -> Either Integer Double -> Either Integer Double 
foo f x = case x of 
    Left i -> Left $ f i 

を、型チェッカーはLeft $ f iのタイプはEither Integer Doubleでなければならないので、それ故に表現f iIntegerでなければならない、困っています。しかし、あなたは、呼び出し側が数値型をそれ自身にマップする関数を渡すかもしれないと言った。たとえば、タイプシグネチャではDouble -> Double関数を渡すことができます。明らかに、このような関数は決してIntegerになることはないので、ここではfのアプリケーションは適切に型付けされていません。

OTOHより高いランク付けタイプのソリューションを使用すると、すべての数値タイプで機能する関数のみを特定のタイプで処理することはできません。たとえば、negateを渡すことはできますが、((1::Integer)+)は渡すことはできません。これは、他のケースではDoubleの値に同じ関数を適用するので、絶対に意味があります。

したがって、2番目の質問に答えるために、上位のランク付けされた型のソリューションは、コードを考慮して正しいものです。 IntegerDoubleに適用したい場合は、明らかに、negateのような関数を渡したいだけです。

ボトムライン:

f :: (a -> a) -> b 

を使用すると、idtailreverse((1::Int)+)のような関数を渡すことができます。

f :: (forall a. a -> a) -> b 

とあなただけidようなタイプの署名を厳密forall a. a->a(モジュロ型変数リネーミング)と関数を渡すことができるが、他のいずれも、上述ありません。

3

これは基本的にスコープの問題です。私たちは、次のタイプのドラフトを比較してみましょう:

foo1は::ヌムA =>( - > A) - > ...

foo2は::(。ヌムA forallを=> A - > A) - > ...

最初の宣言では、コンパイラはNumのインスタンスである1つのタイプaと、その特定のタイプのタイプの関数a -> aを持つことを望んでいます。具体的には、Integerでしか動作しない関数がここに当てはまります。もちろん、そのような関数はあなたのタスクには合わないので、コンパイラは指定した型シグニチャの実装を正当に拒否します。一方、2番目の型シグネチャでは、allのインスタンスがNumの場合に機能するタイプa -> aのファンクションが必要であることが示されています。このタイプは前者に比べてかなり制限されています。回避策として

あなたは二回与えられることに機能を必要とする可能性がある:

foo :: (a -> a) -> (b -> b) -> Either a b -> Either a b 
foo f g = either (Left . f) (Right . g) 
1

それは本当に、スコープの問題です。

元では、fooのインスタンスごとに新しいタイプの変数があります。

foo :: forall a. Num a => (a -> a) -> Either Integer Double -> Either Integer Double 

afooの各インスタンスの全体で同じでなければならず、一方のみNumインスタンスが関与しています。ランク2の多態性バージョンでは、fパラメータのインスタンス化ごとに新しい型の変数があります。

foo :: (forall a. Num a => a -> a) -> Either Integer Double -> Either Integer Double 

インスタンス数と同じように多くのNumインスタンスが話しています。