2017-07-09 12 views
4

ここで問題ありません:これはF#コンパイラの型推論を混乱させるのはなぜですか?

module Seq = 
    let private rnd = Random Environment.TickCount 

    let random = 
     fun (items : 'T seq) -> 
      let count = Seq.length items 
      items |> Seq.nth (rnd.Next count) 

Seq.randomの署名がitems:seq<'T> -> 'Tです。すべての良い。

はい、私はちょうどlet random items = [...]、それはポイントではないことができることを知っています。

ポイントはitemsが突然、私はこれを行う際にタイプseq<obj>に制限されていることである。

module Seq = 
    let random = 
     let rnd = Random Environment.TickCount 
     fun (items : 'T seq) -> 
      let count = Seq.length items 
      items |> Seq.nth (rnd.Next count) 

...つまり、私はクロージャとしてRandomオブジェクトを追加します。 randomにカーソルを合わせると、Intellisenseはその署名がitems:seq<obj> -> objになったことを示しています。

興味深いことに、F#Interactiveでコードを選択して[Alt]+[Enter]を実行すると、署名はseq<'a> -> 'aと表示されます。 WTH ??

ここでは何が起こっているのですか?型推論の混乱と不一致はなぜですか?

答えて

8

これは、いわゆるValue Restrictionによるものです。短い文章を切り詰めると、文法的な値は一般的ではありません。なぜなら、突然変異が発生したときに物事が壊れる可能性があり、コンパイラが不変性を常に確実に証明できるわけではありません。コンパイラは不変性を証明することができ時々

しかしrandomが意味的機能であっても、それは文法的にまだであり、それは重要なものだ、ということに注意してください)。これはあなたの最初の例がうまくいく理由です:letの右辺がまっすぐなラムダ式であるとき、コンパイラはそれが不変であることを確実に伝えることができます。

もう1つの例はlet x = []です。ここでは、コンパイラはnilリスト[]が不変であることを確認できます。一方、let x = List.append [] []は動作しません。その場合、コンパイラは不変性を証明できないためです。

この値の制限の「緩和」は、F#でケースバイケースで行われます。 F#コンパイラは、リテラル、ラムダ式などの特殊なケースを処理するためにのみ使用されますが、一般的に不変性を証明するための本格的なメカニズムはありません。そのため、これらの特別なケースの外に出ると、一般的な値を持つことはできません。

明示的な型引数を追加することによって、技術的にこれを無効にすることができます。論理的に、これはコンパイラに "はい、私はそれが一般的な価値であることを知っています、そして、それは私がそれが意味するものです"と伝えます。

let random<'t> : seq<'t> -> 't = 
    let rnd = Random Environment.TickCount 
    fun items -> 
     let count = Seq.length items 
     items |> Seq.nth (rnd.Next count) 

let x = random [1;2;3] 

しかし、舞台裏で、このような定義は、パラメータなしの一般的な方法にコンパイルされ、そしてあなたは、このような「値」を参照するたびに、メソッドが呼び出されるとリターンされますので、これはまだ、あなたがやりたいことはありませんあなたは新しい機能を - 新しい電話rndはすべての呼び出しのために焼く。言い換えれば、上記のコードは次のようになります。

let random() = 
    let rnd = Random Environment.TickCount 
    fun items -> 
     let count = Seq.length items 
     items |> Seq.nth (rnd.Next count) 

let x = random() [1;2;3] 
+0

kthx [フィラーチャンク] – MiloDC

+0

非常に興味深いです。私はジェネリックletバインディングが無パラメータ関数になっていることに気づいていませんでした。同じことが '未チェック(Unchecked) 'にも当てはまりますか?defaultOf <_> 'と同様のコアライブラリ関数(1つのように見えません)? – Abel

+1

はい。 'Seq.empty'は別の例です。 –

関連する問題