Haskellのデバッグテクニックを簡単に説明します

3570 ワード

穴に入るのは1ヶ月も違わないで、ずっとHaskellの中でこの1回をデバッグすることに関心を持ったことがなくて、1つは複雑なプログラムを書いていないで、大多数はすべてコンパイルの間違いで、調整してコンパイルしたことがあって終わりました.二つ目は手元のいくつかの本で、Learn you a haskellReal World HaskellそしてThe craft of functional ProgrammingはHaskellデバッグのテクニックについて話していません.もちろん、これもHaskellの比較的高い寒さの特質に合っています.いずれもputStrLnという関数を紹介しますが、この関数はIO Monadと一緒に使わなければなりません.便利ではありません.使う場所が多ければ非常に美しくありません.
だから今まで出会ったランタイムエラーは、私はBVM(Brain Virtual Machine,)を採用しています.を選択して検索します.
もちろん、BVMは簡単なプログラムにも適用されます.最近簡単なCompilerを書いて、LLVM IRを生成する時1つのBugに出会って、すべてのVariableタイプのデータに対してすべて失敗して、1つのを返して、loadを試してみる時1つのcasting errorを報告します.
私がBVMを使って脳の中で流れを演じた数百万回も特ニャンがこのバグを見つけられなかったとき.私はすっかり感心して、おとなしくHaskellのデバッグのテクニックを勉強しました.
コマンドプログラミングでは、適切な場所にprint文を挿入してcontext infoを印刷することが基本です.ではハスカルで優雅に実現できるのでしょうか?
The answer is YES!
trace
GHCは実はかなりのdebug方法を内蔵して、具体的にはここを見て、ここで最も簡単で最も実用的な方法を話します——Debug.Tracetraceを使用するのは簡単で、ファイルの先頭import Debug.Traceだけでいいです.
このような関数があるとします.
subStringToIndex :: String -> Int -> String
subStringToIndex s n = take n s 

だから不思議なことに、1つの文字列sと1つの整数nを受け入れて、1つのs[0..
subStringToIndex :: String -> Int -> String
subStringToIndex s n = trace ("para s is " ++ show s)  take n s 

実行すると、次のような出力が表示されます.
para s is "abcde"
ab

見慣れたprintfを再見したような気がしますか?濃厚な親切な風が店を開いた.traceも関数であり、非常に簡単に使用されていることは、そのプロトタイプの宣言から見ることができます.
trace :: String -> a -> a
traceの役割公式ドキュメントでは、文字列と任意のタイプのaのパラメータを受け入れ、この文字列を印刷し、aを返します.
何だ?私を信じて、私が初めて見たとき、あなたと同じ感じでした.
人の話で言えば、Stringパラメータは、あなたが印刷するもので、中にはあなたが必要とするcontext infoの文字列が含まれていますが、aは一般的に式、あるいは関数で、ここにaが実行した結果を表す任意のタイプで書くことができます.traceは文字列を印刷し、2番目のパラメータを結果として返します.
したがって、プログラムの実行については、trace "message : " (f x)と直接f xの効果は同じであり、traceは実行前に関連情報を印刷するだけである.
デバッグdo block
実際、Haskellプログラムの多くの複雑な論理はmonadicの形式で書かれています.よく知られているdo blockですが、次の例を見てみましょう.
monadicSubStringToIndex ::  Int -> State String String
monadicSubStringToIndex n = do
    s1 

今回私たちが操作する文字列は、パラメータを介して渡されたのではなく、wrapがState Monadにある文字列です.もしこの時私たちがdebugmonadicSubStringToIndexに来たらどうすればいいですか?do blockの文ブロックは、現在のmonadを入力として受け入れ、monadを出力しなければならないことを知っています.上記のtraceは、ここでは適用できないことは明らかである.
多くのHaskellの関数のように、traceにもmonadicの双子の兄弟がいます.traceMこれはすべてを簡単にします.
簡単に一言追加するだけです
monadicSubStringToIndex ::  Int -> State String ()
monadicSubStringToIndex n = do
    s  take n s)


main :: IO ()
main = putStrLn $ snd $ runState (monadicSubStringToIndex 2) "abcde"

プログラムを実行し、出力:
original string is "abcde"
ab

コマンド言語のように間違った場所でロゴを打ってdebugを手伝うことに成功しましたそれはそう簡単には理解できませんが.traceMの定義は次のとおりです.
traceM :: Monad m => String -> m ()
traceと比較すると、traceMには印刷する文字列というパラメータが1つしかなく、任意のタイプのmonadを返します.しかし、任意のmonadicの関数には、現在のmonadである隠し入力パラメータがあることに注意してください.したがって、構造上、tracetraceMは一致する.ここからも一般的なHaskellプログラミングは関数呼び出しを主線とし,monadicの書き方はmonadの伝達を主線としていることがわかる.
注意すべき点は、traceでもtraceMでも副作用があることです.使用する場所はpureではなく、referentially transparentではありません.簡単に言えば、Haskell言語の特性を満たすfeatureではないので、デバッグ時にのみ使用できます.
もっと見たい?私の知っていることに注目することができます