3

F# で Clojure Transducers の実装を試していたところ、恐ろしい値制限エラーにすぐに遭遇しました。

トランスデューサーの要点は、構成可能であることです。これはいくつかのサンプルコードです:

type Reducer<'a,'b,'c> = ('a -> 'b -> 'a) -> 'a -> 'c -> 'a

module Transducers =
   [<GeneralizableValue>]
   let inline map proj : Reducer<'result,'output,'input> =
      fun xf ->
        fun result input ->
            xf result (proj input)

   let inline conj xs x = x :: xs
   let inline toList xf input = List.fold  (xf conj) [] input

   let xform = map (fun i -> i + 9) >> map (fun a -> a * 5)
   //let xs = toList xform [1;2] // if you apply this, type will be fixed to 'a list
                                 // which makes xform unusable with eg 'a seq

dotnetfiddle で遊ぶ

GeneralizableValue値の制限を解除するはずでしたが、何もしていないようです。あなたの使命は、このコードを適用せずにコンパイルすることですtoList(型推論は型を に固定する'a listため、同じ xform を で使用することはできませんseq)、xform の型を変更することはありません (少なくとも、そうしないようにする方法ではありません)。構成可能)。これは単に F# では不可能なのでしょうか?

4

3 に答える 3

4

値制限の対象となるかどうかに影響を与える注釈mapを付けるのはなぜですか? (いずれにしても、ラムダで定義されているため、すでに一般化可能です。また、すべての s のポイントがわかりません)。 [<GeneralizableValue>]xformmapinline

要件が次の場合:

  • xformジェネリックである必要がありますが、明示的に注釈が付けられた型関数であってはなりません
  • xform演算子の適用によって定義されます ((>>)この場合)

それならあなたは運が悪いです。xformの本体は一般化可能な式ではないため (F# 仕様の §14.7 を参照)、値の制限がここに適用されます。

さらに、これは理にかなっていると主張します。値の制限が適用されず、 の定義を微調整したと想像してくださいmap

let map proj : Reducer<_,_,_> =
    printfn "Map called!"
    fun xf result input ->
        xf result (proj input)

これらの定義を 1 つずつ入力します。

let xform<'a> : Reducer<'a,int,int> = map (fun i -> i + 9) >> map (fun a -> a * 5)

let x1 = xform (+)
let x2 = xform (*)
let x3 = xform (fun s i -> String.replicate i s)

いつ"Map called!"印刷されると思いますか?実際の行動はあなたの期待と一致していますか? 私の意見では、F# が非値をジェネリック値として扱うよう強制するのは良いことです。

したがって、あなたが望むものを正確に手に入れることはできません。しかし、おそらく、ユース ケースに適した別のエンコーディングが存在するでしょう。すべてのレデューサーが結果の型でジェネリックになる場合は、代わりにこれを行うことができます。

type Reducer<'b,'c> = abstract Reduce<'a> : ('a -> 'b -> 'a) -> 'a -> 'c -> 'a

module Transducers =
    let map proj =
        { new Reducer<_,_> with 
            member this.Reduce xf result input = xf result (proj input) }

    let (>!>) (r1:Reducer<'b,'c>) (r2:Reducer<'c,'d>) =
        { new Reducer<_,_> with 
            member this.Reduce xf result input = (r1.Reduce >> r2.Reduce) xf result input }

    let conj xs x = x :: xs
    let toList (xf:Reducer<_,_>) input = List.fold  (xf.Reduce conj) [] input

    let xform = map (fun i -> i + 9) >!> map (fun a -> a * 5)

残念ながら、使用する前に各演算子をリデューサー レベルに持ち上げる(>>)必要がありますが、これは少なくとも例では機能します。これxformは、もはやジェネリック値ではなく、ジェネリック メソッドを使用した非ジェネリック値であるためです。

于 2014-09-18T19:42:30.393 に答える
4

xform明示的に注釈を付けることはどうですか?

   [<GeneralizableValue>]
   let xform<'t> : Reducer<'t, _, _> = map (fun i -> i + 9) >> map (fun a -> a * 5) >> map (fun s -> s + 1)
于 2014-09-17T23:43:49.847 に答える
3

上で提案したように、エラー メッセージ自体で、引数を明示的に追加できますか?

let xform x = x |> map ...

F# は、ポイント フリー アプローチとのみ相性が良い

于 2014-09-17T23:08:16.580 に答える