8

サーバーサイド Javascriptに関するSteve Yegge投稿へのコメントは、言語における型システムのメリットについての議論を開始し、このコメントは次のように説明しています。

...次のようなものを取得できるHMスタイル システムの例:

expected signature Int*Int->Int but got Int*Int->Int

そのエラーを生成する関数定義 (または 2 つ) と関数呼び出しの例を挙げていただけますか? 大規模なプログラムでデバッグするのはかなり難しいようです。

また、ミランダで同様のエラーが発生した可能性がありますか? (15年以上使っていないので記憶があいまいです)

4

3 に答える 3

8

静的型付けに関する Yegge (および Ola Bini の) の意見を参考にします。静的型付けの利点を理解できれば、選択したプログラミング言語の型システムがどのように機能するかがわかります。

IIRC、ML はタプルに '*' 構文を使用します。<type> * <type> は 2 つの要素を持つタプル型です。したがって、(1, 2) は int * int 型になります。

Haskell と ML はどちらも -> を関数に使用します。ML では、int * int -> int は、int と int のタプルを取り、それを int にマップする関数の型になります。

別の言語から ML に来たときに Ola が引用したものと漠然と似ているエラーが表示される理由の 1 つは、C や Pascal で行うように、括弧とコンマを使用して引数を関数に渡そうとした場合です。 2 つのパラメーターを取ります。

問題は、関数型言語は通常、関数を返す関数として複数のパラメーターを持つ関数をモデル化することです。すべての関数は引数を 1 つだけ取ります。関数が 2 つの引数を取る必要がある場合は、代わりに引数を取り、最終結果を返す単一の引数の関数を返します。これらすべてを読みやすくするために、関数の適用は単純に結合して行います (つまり、式を互いに並べて配置します)。

したがって、ML の単純な関数 (注: ML として F# を使用しています) は、次のようになります。

let f x y = x + y;;

タイプがあります:

val f : int -> int -> int

(整数を取り、それ自体が整数を取り、整数を返す関数を返す関数。)

ただし、単純にタプルで呼び出すと、次のようになります。

f(1, 2)

... int*int を int を期待するものに渡したため、エラーが発生します。

これが、オラが中傷を投げかけようとしていた「問題」だと思います。ただし、彼が考えているほど問題は深刻ではないと思います。確かに、C++ テンプレートではさらに悪化します。

于 2008-11-27T20:55:25.773 に答える
4

これは、エラー メッセージを明確にするための括弧の挿入に失敗した不適切に作成されたコンパイラを参照していた可能性があります。具体的には、関数は と のタプルを期待してintいましたが、 と のタプルとから の関数intを渡しました。より具体的に(MLで):intintint

fun f g = g (1, 2);

f (42, fn x => x * 2)

これにより、次のような型エラーが生成されます。

予期されたタイプint * int -> int、得られたタイプint * (int -> int)

括弧が省略されている場合、このエラーは厄介なほどあいまいになる可能性があります。

この問題は Hindley-Milner に固有のものではないことに注意してください。実際、 HM に固有の奇妙な型エラーは思い浮かびません。少なくとも、与えられた例のようなものはありません。オラはただ煙を吹いていただけだったのではないかと思います。

于 2008-11-27T21:17:01.487 に答える
3

多くの関数型言語では、変数を再バインドできるのと同じ方法で型名を再バインドできるため、実際には、このようなエラーが発生しやすくなります。特にt、異なるモジュールで型にやや一般的な名前 ( など) を使用している場合は特にそうです。OCaml での簡単な例を次に示します。

# let f x = x + 1;;
val f : int -> int = <fun>
# type int = Foo of string;;
type int = Foo of string
# f (Foo "hello");;
This expression has type int but is here used with type int

ここで行ったことintは、組み込み型と互換性のない新しい型に型識別子を再バインドすることですint。もう少し努力すると、多かれ少なかれ上記と同じエラーが発生する可能性があります。

# let f g x y = g(x,y) + x + y;;
val f : (int * int -> int) -> int -> int -> int = <fun>
# type int = Foo of int;;
type int = Foo of int
# let h (Foo a, Foo b) = (Foo a);;
val h : int * int -> int = <fun>
# f h;;
This expression has type int * int -> int but is here used with type
  int * int -> int
于 2009-06-07T15:14:44.757 に答える