2

编辑:请注意,正如 Daniel 和 latkin 在下面的答案和评论中指出的那样,这个问题涉及 F# 中的一个错误,该错误似乎已在 2014 年初修复。

我正在尝试为 Observable.StartWith 编写一个咖喱包装器。我正在使用预发布的 Reactive Extensions 2.0 和 VS11 beta。我想要的结果是startWith : 'a -> IObservable<'a> -> IObservable<'a>. 显而易见的实现类似于:

let startWith 
        (value : 'a) 
        (observable : IObservable<'a>) 
        : IObservable<'a> =
    Observable.StartWith(observable, [| value |])

Observable.StartWith 的预期重载是StartWith<'TSource>(source : IObservable<'TSource>, params values: 'TSource[]) : IObservable<'TSource>.

编译器抛出一个令人困惑的错误:This method expects a CLI 'params' parameter in this position. 'params' is a way of passing a variable number of arguments to a method in languages such as C#. Consider passing an array for this argument.

正在传递一个数组。我还尝试通过省略传递数组,[| |]这会导致唯一重载解析失败。(大概是由于可能'aSystem.Reactive.Concurrency.IScheduler其他重载匹配。)我还尝试使用 F# 2.0/VS2010,它给出了相同的结果。我找不到任何关于这种情况或编译器错误消息的在线讨论。

我想不出任何其他方式来实现这一点。请注意,在可以确定类型参数的情况下,这不是问题。例如,let prependZero : int -> IObservable<int> -> IObservable<int> = fun n o -> o.StartWith(n)工作正常。但是通用版本会很好。

4

2 回答 2

3

围绕泛型参数数组的类型推断似乎存在问题。即使是不涉及重载解析的简单案例也有问题:

type A() = 
  static member M<'T>([<ParamArray>] args: 'T[]) = args

//None of these work
let m1 arg = A.M([|arg|])
let m2 args = A.M(args)
let m3<'T> (args:'T[]) = A.M<'T>(args)

非通用版本工作:

type B() = 
  static member M([<ParamArray>] args: obj[]) = args

//Both of these are okay
let m1 arg = B.M([|arg|])
let m2 args = B.M(args)

编辑

我给 fsbugs 发了电子邮件,他们回复说这是一个错误。以下是他们建议的一些解决方法。

let m1 arg = A.M<obj>([|arg|])
let m2 args = A.M<obj>(args)
let m3 (args:obj[]) = A.M<obj>(args)
let m4 (arg:obj) = A.M<obj>(arg)
let m5 arg1 arg2 = A.M<obj>(arg1,arg2)
let m6 (arg1:'T) = A.M<'T>(arg1)
let m7 (arg1:'T) (arg2:'T) = A.M<'T>(arg1,arg2)
let m8 (arg1:'T) (arg2:'T) = A.M(arg1,arg2)
let m9 (arg1:'T) = A.M(arg1)
let m10<'T> arg1 arg2 = A.M<'T>(arg1,arg2)
let m11<'T> (arg1:'T) (arg2:'T) = A.M<'T>(arg1,arg2)
于 2012-06-17T03:34:10.703 回答
1

您不需要将您的 single 包装value到单个元素数组中以使其匹配 的最后一个ParamArray参数Observable.StartWith,只需标量值即可(这些示例可能有助于理解原因)。

但是泛型类型value在两个可用的重载之间产生了歧义Observable.StartWith。可以通过强制三个参数重载来实现消歧,方法是将两个参数重载中的隐式类型显式放置IScheduler到参数列表中,在 前面加上value,如下所示:

let startWith (value: 'a) observable =
    Observable.StartWith(observable, Scheduler.CurrentThread, value) 

现在您的代码应该可以编译并工作了。快速检查证实了这一点:

Observable.Range(1,2)
|> startWith 10
|> fun x -> x.Subscribe(printf "%d ")

按预期输出10 1 2

更新

对于 Rx 2.0 beta,Scheduler参考会略有不同,其余答案保持不变:

let startWith (value: 'a) (observable: IObservable<'a>) = 
    Observable.StartWith(observable, Concurrency.Scheduler.CurrentThread, value)
于 2012-06-17T04:35:56.983 回答