编辑:请注意,正如丹尼尔和拉特金在下面的答案和评论中指出的那样,这个问题涉及F#中的一个错误,似乎已在2014年初修复过。
我正在尝试为Observable.StartWith编写一个curried包装器。我正在使用预发布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
。
我我传递一个数组。我还尝试通过省略[| |]
来尝试不传递数组,这会导致唯一的重载解析失败。 (可能是因为'a
可能是System.Reactive.Concurrency.IScheduler
,与其他重载相匹配。)我也尝试使用F#2.0 / VS2010,它给出了相同的结果。我找不到任何关于此类情况或编译器错误消息的在线讨论。
我想不出任何其他方式来实现这一点。请注意,在可以确定类型参数的情况下,这不是问题。例如,let prependZero : int -> IObservable<int> -> IObservable<int> = fun n o -> o.StartWith(n)
工作正常。但通用版本会很好。
答案 0 :(得分:3)
围绕通用param数组的类型推断看起来像一个问题。即使是一个不涉及重载解析的简单案例也存在问题:
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)
答案 1 :(得分:1)
您不需要将单个value
包装到单个元素数组中,以使其与ParamArray
的最后一个Observable.StartWith
参数匹配,只需标量值即可({{3可能有助于理解为什么)。
但是,value
的泛型类型会在Observable.StartWith
的两个可用重载之间产生歧义。可以通过强制these samples来实现消歧,方法是将两个参数重载的隐式类型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)