7

高階関数に(私が思うに)正しい型を指定すると、OCamlコンパイラはその関数の2回目の使用を拒否します。

コード

let foo ():string  =
    let f: ('a -> string) -> 'a -> string = fun g v -> g v
    in let h = string_of_int
    in let i = string_of_float
    in let x = f h 23
    in let y = f i 23.0
    in x ^ y

次のエラーメッセージが表示されます

ファイル"test.ml"、6行目、文字14〜15:
エラー:この式の型はfloat->stringです
       しかし、int->string型の式が期待されていました

したがって、の最初の使用法はf、最初のパラメータのタイプをに固定しているようint -> stringです。理解できました。しかし、私が得られないのは、の型制限を省略fすると問題が修正されるということです。

let foo ():string  =
    let f g v = g v
    in let h = string_of_int
    in let i = string_of_float
    in let x = f h 23
    in let y = f i 23.0
    in x ^ y

fまた、グローバルスコープに移行すると、問題も修正されます。

let f: ('a -> string) -> 'a -> string = fun g v -> g v

let foo ():string  =
  let h = string_of_int
  in let i = string_of_float
  in let x = f h 23
  in let y = f i 23.0
  in x ^ y

最初の例がコンパイルされないのに、後の例はコンパイルされるのはなぜですか?

4

3 に答える 3

9

問題を説明する簡単な例を使用しましょう。

# let cons0 (x : 'a) (y : 'a list) = x :: y;;
val cons0 : 'a -> 'a list -> 'a list = <fun>
# let cons1 (x : 'a) (y : 'a list) = x :: y in cons1 1 [];;
- : int list = [1]
# let cons2 (x : 'a) (y : 'a list) = x :: y in (cons2 1 [], cons2 true []);;
This expression has type bool but is here used with type int
# let cons3 x y = x :: y in (cons3 1 [], cons3 true []);;
- : int list * bool list = ([1], [true])

cons0は、グローバルスコープで定義されたポリモーフィック関数の定義です。::これは、オペレーターにとっては些細なラッパーです。当然のことながら、定義は機能します。スコープがボディ内の式に限定されていることを除いて、はとほぼcons1同じです。スコープの変更は無害に見えます、そして確かに、それはタイプチェックします。これも同じ関数であり、型の注釈はありません。これを本体で多形的に使用できます。cons0incons3in

では、何が問題になっていcons2ますか?問題は範囲です'a:それはトップレベルのフレーズ全体です。定義するフレーズのセマンティクスは次のとおりcons2です。

for some type 'a, (let cons2 (x : 'a) (y : 'a list) = ... in ...)

(による)および(による)と'a互換性がある必要があるため、のインスタンス化は不可能です。したがって、フレーズの型が正しくありません。intcons3 1 []boolcons3 true []'a

通常の型推論アルゴリズムの観点からML型付けについて考えたい場合は、明示的なユーザー変数ごとに、統合アルゴリズムに一連の制約が導入されます。'a = "type of the parameter x"ここで、制約はとです' = "type of the parameter y"。しかし、の範囲は'aフレーズ全体であり、内部の範囲では一般化されていません。したがってintbool両方とも、一般化されていないものに統合され'aます。

最近のバージョンのOCamlでは、スコープ付き型変数が導入されています(Niki Yoshiuchiの回答のように)。以前のバージョン(≥2.0)のローカルモジュールでも同じ効果が得られた可能性があります。

let module M = struct
    let cons2 (x : 'a) (y : 'a list) = x :: y
  end in
(M.cons2 1 [], M.cons2 true []);;

(標準のMLers注:これはOCamlとSMLが異なる1つの場所です。)

于 2010-12-14T20:44:32.157 に答える
4

これは本当のヘッドスクラッチャーであり、コンパイラのバグであっても驚かないでしょう。そうは言っても、タイプに明示的に名前を付けることで、やりたいことができます。

let f (type t) (g: t->string) (v: t) = g v in

マニュアルから:http://caml.inria.fr/pub/docs/manual-ocaml/manual021.html#htoc108

編集:

これも機能します:

let f g v:string = g v in

これはあなたが探している型署名を持っています:('a -> string) -> 'a -> string

引数のタイプに注釈を付けると機能しないのは不思議です。

編集:

ポリモーフィック型の注釈には、特別な構文があります。

let f: 'a. ('a -> string)-> 'a -> string = fun g v -> g v in

また、タイプは多形である必要があります:http: //caml.inria.fr/pub/docs/manual-ocaml/manual021.html#toc79

于 2010-12-14T19:50:49.957 に答える
-2

基準点として、同等のF#

let foo ():string  = 
    let f: ('a -> string) -> 'a -> string = fun g v -> g v 
    let h = string 
    let i = string
    let x = f h 23 
    let y = f i 23.0 
    x ^ y 

コンパイルします。

于 2010-12-14T20:46:57.450 に答える