价值限制困扰

时间:2014-09-17 22:49:28

标签: f# transducer

我正在尝试在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

Play on dotnetfiddle

GeneralizableValue应该解除价值限制,但似乎什么也没做。您的任务是在不应用toList的情况下编译此代码(类型推断会将类型修复为'a list,因此您无法使用与seq相同的xform)而无需更改类型xform(至少不是为了使它不可组合)。这在F#中根本不可能吗?

3 个答案:

答案 0 :(得分:4)

明确注释xform怎么样?

   [<GeneralizableValue>]
   let xform<'t> : Reducer<'t, _, _> = map (fun i -> i + 9) >> map (fun a -> a * 5) >> map (fun s -> s + 1)

答案 1 :(得分:4)

为什么用map注释[<GeneralizableValue>]会影响xform是否受价值限制? (在任何情况下,map已经可以推广,因为它是由lambda定义的;而且我没有看到所有inline s的重点。

如果您的要求是:

  • 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不再是通用值,但是使用泛型方法的非泛型值。

答案 2 :(得分:3)

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

let xform x = x |> map ...

F#只能与无点方法一起发挥