web-dev-qa-db-ja.com

命令型言語デバッガーと関数型言語デバッガーの違い

これまでは、Pascal、C、C++、Javaなどの命令型言語を運用環境で使用してきたため、これらの言語のデバッガー(Turbo Pascal、Turbo C、GDB/DDD、Visual Studio、Eclipse)。

命令型言語のデバッガーは通常、

  • ステートメントにブレークポイントを設定し、次のブレークポイントに到達するまでプログラムを実行します。
  • 関数/メソッド呼び出しを入力するか、次のステートメントにスキップするオプションを使用して、一度に1つのステートメントでプログラムを実行します。

プログラムの実行を一時停止すると、プログラムの現在の状態を調べることができます。

  • スタックの内容を調べて、関数呼び出しの現在の入れ子を確認します。
  • ローカル変数、実際の関数パラメーター、グローバル変数などを検査します。

私は実稼働環境で関数型プログラミング言語を扱ったことがないので、そのような言語のデバッガーがどのように見えるかを理解しようとしていました。例えば:

  • 命令型言語のようなステートメントがない場合、デバッガーはプログラムをどのように処理しますか?どこにブレークポイントを設定しますか?
  • Haskellのような遅延評価のある言語では、関数呼び出しのシーケンスが熱心な言語のシーケンスと異なる場合があります。これは、コールスタックを理解しようとするときに大きな違いをもたらしますか?
  • 変数はどうですか?状態がないので、デバッガーは現在のスコープ(?)の変数バインディングのみを表示することを想像します。つまり、プログラムをステップ実行しても、値を変更するローカル変数またはグローバル変数はありません。

要約すると、関数型言語デバッガーと命令型言語デバッガーを明確に区別する一般的な共通機能はありますか?

6
Giorgio

内部的には、ラムダ計算にルーツが多い関数型言語は、いくつかの抽象的な計算モデルに従って厳密にコンパイルされてコードにコンパイルされます。 haskellの場合はSTGマシン、ocaml/caml-lightの場合はCAMマシンです。この実行モデルには通常、ローカル変数のようなものがあり、実行の明確なルートがあり、ソースコードは明確で明確に定義されたアルゴリズムによって抽象マシンのコードに変換されます。したがって、ブレークポイントを(たとえば)コードに挿入でき、ローカル変数のようなものを、ブレークポイントを含む式を評価するときに決定できます。

しかし、haskellやその他の遅延言語の場合、問題は、(プロファイリングを除いて)あまり役に立たないことです。 REPLは、関数本体の定義から副作用がなくなるため、式を簡単にコーディングできるため、関数をローカルでデバッグする必要がなく、アサートでエラーを確実に特定できます。通常、バグの実際のソース不適切な場所での怠惰によるスペースリークであり、ヒープ構造を検査する完全に異なるツールが必要です(一部は利用可能です)。

3
permeakra

命令型言語のようなステートメントがない場合、デバッガーはプログラムをどのように処理しますか?どこにブレークポイントを設定しますか?

ほとんどの関数型言語にはまだステートメントがあります。彼らはいつものように働きます。それ以外の場合は、通常、関数呼び出しの入り口、またはときどき部分式になります。

Haskellのような遅延評価のある言語では、関数呼び出しのシーケンスが熱心な言語のシーケンスと異なる場合があります。これは、コールスタックを理解しようとするときに大きな違いをもたらしますか?

私は多くはしませんでしたが、私が見たものからそれが実際に大きな違いを生むことはありません。最悪の場合でも、呼び出しスタック自体がバグの原因を特定するのにほとんど役に立たない、並行命令型プログラムと同様に動作します。

変数はどうですか?状態がないので、デバッガーは現在のスコープ(?)の変数バインディングのみを表示することを想像します。つまり、プログラムをステップ実行しても、値を変更するローカル変数またはグローバル変数はありません。

繰り返しますが、ほとんどの関数型言語には少なくともいくつかの状態があります。より大きな影響は、プログラムを奇妙なものにする原因となる、遅延構成の評価に沿ったものです。ただし、これはPythonジェネレーターやC#列挙型など)の命令型言語の遅延構成とそれほど変わりません。


結局のところ、私の経験には大きな違いはありません。

3
Telastyn