我正在尝试在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
GeneralizableValue
应该解除价值限制,但似乎什么也没做。您的任务是在不应用toList
的情况下编译此代码(类型推断会将类型修复为'a list
,因此您无法使用与seq
相同的xform)而无需更改类型xform(至少不是为了使它不可组合)。这在F#中根本不可能吗?
答案 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#只能与无点方法一起发挥