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 与 a 一起使用seq)并且不更改 xform 的类型(至少不会以某种方式使其不可组合)。这在 F# 中根本不可能吗?

4

3 回答 3

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 回答
4

为什么注释map[<GeneralizableValue>]影响是否xform受值限制?(无论如何,map由于它是由 lambda 定义的,因此已经可以推广;我也没有看到所有inlines 的意义)。

如果您的要求是:

  • xform必须是泛型的,但不是显式注释的类型函数
  • xform由运算符的应用程序定义((>>)在这种情况下)

那你就不走运了;xform的主体不是可泛化的表达式(请参阅 F# 规范中的 §14.7),因此此处适用值限制。

此外,我认为这是有道理的。想象一下,值限制不适用,并且我们调整了 的定义map

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

现在一一输入这些定义:

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# 迫使您不遗余力地将非值视为通用值是件好事。

所以你不会得到你想要的。但也许有一种不同的编码方式同样适用于您的用例。如果每个reducer 在结果类型中都是通用的,那么您可以这样做:

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)

不幸的是,您必须先将每个运算符提升(>>)到 reducer 级别,然后才能使用它,但这至少适用于您的示例,因为xform不再是泛型值,而是具有泛型方法的非泛型值。

于 2014-09-18T19:42:30.393 回答
3

如上所述,在错误消息本身中,您可以显式添加参数吗?

let xform x = x |> map ...

F# 只与无点方法配合得很好

于 2014-09-17T23:08:16.580 回答