5

「値の制限」は、実際には高次の関数型プログラミングがないことを意味しますか?

HOP を少し実行しようとするたびに、VR エラーが発生するという問題があります。例:

let simple (s:string)= fun rq->1 
let oops= simple ""

type 'a SimpleType= F of (int ->'a-> 'a)
let get a = F(fun req -> id)  
let oops2= get ""

それがVRの特定の実装の問題なのか、型システムに突然変異を含まない可変型推論言語で解決策がない一般的な問題なのかを知りたいです。

4

3 に答える 3

6

「値の制限」とは、高次の関数型プログラミングがないことを意味しますか?

絶対違う! 値の制限は、高階関数型プログラミングをほとんど妨げません。それが行うことは、多相関数(高階関数ではなく) の一部の適用を最上位で制限することです。


あなたの例を見てみましょう。あなたの問題は、oopsandoops2がアイデンティティ関数であり、 type を持っていることforall 'a . 'a -> 'aです。つまり、それぞれがポリモーフィックな値です。しかし、右側はいわゆる「構文値」ではありません。関数アプリです。(もしそうなら、型システムを破壊する可変参照とリストを使用してハッキーな関数を構築できるため、関数アプリケーションは多相値を返すことはできません。つまり、終了関数型 type を書くことができますforall 'a 'b . 'a -> 'b

幸いなことに、ほとんどすべての実際のケースで、問題のポリモーフィック値は関数であり、イータ展開によって定義できます。

let oops x = simple "" x

このイディオムには実行時間のコストがかかるように見えますが、インライン ライナーとオプティマイザーによっては、コンパイラーによって取り除くことができます。

このoops2例は、値コンストラクターをパックおよびアンパックする必要があるため、より面倒です。

let oops2 = F(fun x -> let F f = get "" in f x)

これは非常に面倒ですが、無名関数fun x -> ...は構文値でありF、データ型コンストラクターであり、構文値に適用されるコンストラクターも構文値であり、ボブは叔父です。のパッキングとアンパッキングFはすべて恒等関数にコンパイルされるため、 とまったく同じマシン コードにコンパイルされoops2ます。oops

None実行時の計算でやのような多態的な値を返したい場合は、さらに厄介です[]。Nathan Sanders が示唆しているように、次のような単純な式で値の制限に違反する可能性がありますrev []

Standard ML of New Jersey v110.67 [built: Sun Oct 19 17:18:14 2008]
- val l = rev [];
stdIn:1.5-1.15 Warning: type vars not generalized because of
   value restriction are instantiated to dummy types (X1,X2,...)
val l = [] : ?.X1 list
-  

高次のものは何もありません!それでも、値の制限が適用されます。

実際には、値の制限は、高階関数の定義と使用に何の障害にもなりません。あなたは単にイータ展開します。

于 2010-04-16T01:50:05.273 に答える
2

値制限の詳細がわからなかったので、この記事を検索して見つけました。関連する部分は次のとおりです。

もちろん、プログラムで式rev []を記述することはないので、多形でないことは特に問題ではありません。しかし、関数呼び出しを使用して関数を作成するとどうなるでしょうか。カレー関数では、これを常に行います。

- val revlists = map rev;

ここで、revlistは多形である必要がありますが、値の制限によって混乱します。

- val revlists = map rev;
stdIn:32.1-32.23 Warning: type vars not generalized because of
   value restriction are instantiated to dummy types (X1,X2,...)
val revlists = fn : ?.X1 list list -> ?.X1 list list

幸い、revlistをポリモーフィックにするために使用できる簡単なトリックがあります。revlistの定義を次のように置き換えることができます

- val revlists = (fn xs => map rev xs);
val revlists = fn : 'a list list -> 'a list list

(fn xs => map rev xs)は構文値であるため、すべてが正常に機能するようになりました。(同等に、より一般的な楽しい構文を使用することもできます。

- fun revlists xs = map rev xs;
val revlists = fn : 'a list list -> 'a list list

同じ結果になります。)文献では、関数値の式eを(fn x => ex)に置き換えるトリックは、eta展開として知られています。値の制限に対処するには、通常、eta展開で十分であることが経験的にわかっています。

要約すると、ポイントフリープログラミングほど高次プログラミングが制限されているようには見えません。これは、HaskellコードをF#に変換するときに私が抱えている問題のいくつかを説明しているかもしれません。


編集:具体的には、最初の例を修正する方法は次のとおりです。

let simple (s:string)= fun rq->1 
let oops= (fun x -> simple "" x)     (* eta-expand oops *)

type 'a SimpleType= F of (int ->'a-> 'a)
let get a = F(fun req -> id)
let oops2= get ""

型コンストラクターが邪魔をしているので、私はまだ2番目のものを理解していません。

于 2010-04-15T12:57:00.090 に答える
2

F# のコンテキストでのこの質問に対する回答を次に示します。要約すると、F# では、型引数をジェネリック (= ポリモーフィック) 関数に渡すことは実行時の操作であるため、一般化しても実際には型安全です (つまり、実行時にクラッシュすることはありません)。ただし、このように一般化された値の動作は驚くべきものになる可能性があります。

F# のこの特定の例では、型注釈と明示的な型パラメーターを使用して一般化を回復できます。

type 'a SimpleType= F of (int ->'a-> 'a)
let get a = F(fun req -> id)  
let oops2<'T> : 'T SimpleType = get ""
于 2010-05-06T04:19:24.843 に答える