2012-03-30 8 views
7

:もちろんHaskell関数でパターンマッチングをデバッグすることは可能ですか?私はタイプ</p> <pre><code>data Expr = Const Double | Add Expr Expr | Sub Expr Expr </code></pre> <p>を定義し、<code>Eq</code>型クラスのインスタンスとして、それを宣言した

instance Eq Expr where 
    (Add (Const a1) (Const a2)) == Const b = a1+a2 == b 
    (Add (Const a1) (Const a2)) == (Add (Const b1) (Const b2)) = a1+a2 == b1 + b2 

、表現Sub (Const 1) (Const 1) == Const 0の評価が失敗します。実行時にパターンマッチングプロセスをデバッグして、失敗していることを検出する方法はありますか?私はHaskellがどのように==の議論を取り入れてパターンを歩いていくのか見たいと思います。まったく可能ですか?

+4

GHCはコンパイル時に問題を検出できます。 '-Wall'を使ってコンパイルすると、不完全なパターンについて警告し、あなたが見逃したケースを表示します。 – hammar

+6

正確なオプションは '-fwarn-incomplete-patterns'です。どのように動作するかは、http://stackoverflow.com/questions/7883023/algorithm-for-type-checking-ml-like-pattern-matchingで確認してください。ところで、あなたの例では、 'eval :: Expr - > Double'と' x == y = eval x == eval y'と書くことをお勧めしますが、まだ非標準です。 – sdcvvc

+0

私は警告について知っています。私は、例えば、私が考えなければならないパターンがなぜ一致しなければならないのかを知りたいと思います。 –

答えて

2

編集:質問への本当の答えを提供する...

私はパターンが一致しているかを確認する最も簡単な方法はそうのような、traceステートメントを追加することです見つける:

import Debug.Trace 

instance Eq Expr where 
    (Add (Const a1) (Const a2)) == Const b = trace "Expr Eq pat 1" $ a1+a2 == b 
    (Add (Const a1) (Const a2)) == (Add (Const b1) (Const b2)) = trace "Expr Eq pat 2" $ a1+a2 == b1 + b2 
    -- catch any unmatched patterns 
    l == r = error $ "Expr Eq failed pattern match. \n l: " ++ show l ++ "\n r: " ++ show r 

あなたドン場合そうでなければ他に類のないパターンをキャッチするための最終的なステートメントを含めると、実行時例外が発生しますが、どのデータを取得するのがより有用かを知ることができます。その後、それが以前のパターンと一致しない理由は簡単です。

もちろん、これをプロダクションコードに残したくない場合もあります。必要に応じてトレースを挿入し、終了したらトレースを削除します。また、CPPを使用してプロダクションビルドの外に置くこともできます。

私はパターンマッチングがこれについて間違った方法だとも言いたいと思います。あなたはパターンの数の組み合わせの爆発で終わるでしょう。急速に手に負えないほど成長します。たとえば、ExprのインスタンスをFloatにしたい場合は、さらにいくつかのプリミティブコンストラクタが必要になります。

代わりに、おそらくインタープリタ機能interpret :: Expr -> Doubleを持っているか、少なくとも1つ書くことができます。そして、あなたはパターンマッチングにより

instance Eq Expr where 
    l == r = interpret l == interpret r 

を定義することができ、あなたは本質的Eqインスタンスであなたの解釈機能を再度書いています。 Ordインスタンスを作成したい場合は、もう一度解釈関数を書き直してください。

+0

追跡は道のりです。 'Expr'のいくつかのインスタンスは関数になる可能性があるので、doubleを評価することはできません。 –

2

マッチングの失敗の例をご希望の場合は、QuickCheckをご覧ください。あなたのニーズに完全に合うと思われる再帰的なデータ型の生成とテストについては、manual(テストデータのサイズ)の例があります。

-Wallフラグは一致しないパターンのリストを表示しますが、QuickCheckを実行すると、指定した命題が失敗するような入力データの例が表示されます。 たとえば、Exprのジェネレータを書き、quickCheckに入力すると、Exprがそれ自身と等しいかどうかをチェックする命題prop_expr_eq :: Expr -> Boolが得られます。一致しない入力の最初の例として、非常に早くConst 0.0を取得します。

import Test.QuickCheck 
import Control.Monad 

data Expr = 
    Const Double 
    | Add Expr Expr 
    | Sub Expr Expr 
    deriving (Show) 

instance Eq Expr where 
    (Add (Const a1) (Const a2)) == Const b = a1+a2 == b 
    (Add (Const a1) (Const a2)) == (Add (Const b1) (Const b2)) = a1+a2 == b1 + b2 

instance Arbitrary Expr where 
    arbitrary = sized expr' 
     where 
     expr' 0 = liftM Const arbitrary 
     expr' n | n > 0 = 
      let subexpr = expr' (n `div` 2) 
      in oneof [liftM Const arbitrary, 
         liftM2 Add subexpr subexpr, 
         liftM2 Sub subexpr subexpr] 

prop_expr_eq :: Expr -> Bool 
prop_expr_eq e = e == e 

ご覧のとおり、テストを実行すると、平等テストが間違っていることを証明する反例が得られます。私はこれがちょっと残忍かも知れませんが、あなたが良いことを書いた場合の利点は、パターン一致の徹底性だけでなく、任意のプロパティを調べるコードの単体テストも取得できることです。

*Main> quickCheck prop_expr_eq 
*** Failed! Exception: 'test.hs:(11,5)-(12,81): Non-exhaustive patterns in function ==' (after 1 test): 
Const 0.0 

PS:QuickCheckとのユニットテストについてのもう一つの良い読書は無料の本real world haskellです。

1

複雑なパターンをより単純なパターンに分割して、traceを使用して、何が起こっているのかを確認できます。このようなもの:

instance Eq Expr where 
    x1 == x2 | trace ("Top level: " ++ show (x, y1)) True, 
       Add x11 x12 <- x1, 
       trace ("First argument Add: " ++ show (x11, x12)) True, 
       Const a1 <- x11, 
       trace ("Matched first Const: " ++ show a1) True, 
       Const a2 <- x12, 
       trace ("Matched second Const: " ++ show a2) True, 
       Const b <- x2 
       trace ("Second argument Const: " ++ show b) True 
      = a1+a2 == b 

これは少し必死ですが、絶望的な時は絶望的な措置が必要です。 :) ハスケルに慣れている人はめったにいないでしょうが、これを行う必要があります。