4

紛らわしいタイトルですみません。私は楽しみのために Haskell でパーサー コンビネーター ライブラリを書いています。関連する型の注釈と定義をすべて (私が思うに!) 以下に示します。

data Parser a = Parser (State -> Reply a)

parse :: Parser a -> [Char] -> Either ParseError a

nil :: Parser [a]
nil = Parser $ \state -> Ok [] state

基本的に、parse関数は a がラップアラウンドする関数をParser現在の状態に適用し、解析が成功した場合は結果を an にラップしEitherます。nilパーサーは状態を受け取り、空のリストの解析が成功したことを返します。だから私たちは持っているべきです、

parse nil "dog" == Right []

実際、これらすべてが存在するモジュールをロードすると、コンパイルされ、True と評価されます。

ただし、実際にはライブラリでいくつかの QuickCheck テストを実行しようとしているので、次のように書きました。

import Parsimony
import Test.QuickCheck

prop_nil :: [Char] -> Bool
prop_nil xs = parse nil xs == Right []

これはコンパイルに失敗します! 次のエラーがスローされます。

No instance for (Eq a0) arising from a use of `=='
The type variable `a0' is ambiguous

この時点で、式が評価されたときに正常に機能するのに、パラメーター化されたバージョンでコンパイルできない理由について、私はほとんど混乱しています。

4

3 に答える 3

9

nilはポリモーフィックであり、ポリモーフィックでもあるためRight []、GHC は type の式を持ちBoolますが、途中でいくつかのバインドされていない型変数があります。GHC は、どの具象型を使用すればよいかわからないため、転覆して死亡します。GHCi は、良くも悪くも、[()]そのデフォルト ルールにより、推論またはそのようなことを行います。これは ghci の奇妙な癖の 1 つで、自動的にデフォルトの型変数になります。

これを修正するには、単に強制的にa手動でバインドします

-- It's important that whatever you force it to actually is comparable
-- eg there should be an instance like
instance Eq ParseError where
-- Otherwise you're kinda stuck.

prop_nil xs = parse nil xs == (Right xs :: Either ParseError String)

PS パーシモニーという名前のパーサー ライブラリが気に入っています。頑張ってください!

于 2013-07-22T05:07:47.887 に答える
7

問題は、 の型が であるということnilですParser [a]parse nil xsタイプもそうですEither ParseError [a]Right []最も一般的なタイプEither l [a]です。それを と比較すると、 がparse nil xs強制的にlになりますParseErrorが、リスト内の型はまだ完全に制約されていません。これ以上のコンテキストがなければ、完全にポリモーフィックなままです。それaは必ずしもEq型クラスのメンバーではなく、たとえそうであっても、の実装に使用するインスタンスを知る方法がないため、これら 2 つの条件で==呼び出すことは有効ではありません==

現実的なプログラムでは、結果を何かに使用するという事実によって、この問題から救われる可能性があります。これにより、その特定の発生が、それを使用するものと一致するように強制されます。それはおそらく の実装を持つ具体的な型でしょうEq

モジュールのロードについて話すときは、GHCI インタープリターを意味していると思います。GHCI は、いくつかの追加のデフォルト規則を追加します。特に、制約のない型変数 (トップレベル関数の型ではない) を にデフォルト設定する傾向がある()ため、あいまいな型変数について頻繁に文句を言う必要はありません。

GHCi での対話型セッションは、完全にコンパイルされた現実的なモジュールよりもはるかに頻繁にあいまいな型変数に遭遇する傾向があります。GHCi はデフォルト設定ルールを拡張して、それらがより頻繁に機能するようにしました (ただし、ユーザーが別の型を予期していたときにエラーを次の参照まで遅らせるだけであり、GHCi と GHC の違いはしばしば混乱を引き起こします)。

テスト スニペットでも同様の問題が発生する可能性があります。ポリモーフィック関数をテストしている場合、関数を実際に意図的に使用する場合とは異なり、型推論が機能するように型の一部を十分に制約しないことがよくあります。しかし、GHCi の拡張されたデフォルト規則がなければ、この問題は、型を恣意的に選択してマスクするのではなく、問題の場所で実際のあいまいな型エラーとして現れます。

これを修正するには、型注釈を追加してリストの型を修正するだけです。parse nil xsまたはの完全な型を宣言するかRight []、右側で空のリスト リテラルの型を宣言するだけです。このような何かがうまくいくはずです:

prop_nil :: [Char] -> Bool
prop_nil xs = parse nil xs == Right ([] :: [Int])
于 2013-07-22T05:21:52.057 に答える