首页 文章

使用F#(Observable.StartWith)中的'params'调用泛型函数

提问于
浏览
2

Edit :请注意,正如丹尼尔和拉特金在下面的答案和评论中指出的那样,这个问题涉及F#中的一个错误,似乎已经在2014年初修复了 .

我'm trying to write a curried wrapper for Observable.StartWith. I'm使用预发布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,它给出了相同的结果 . 我找不到任何关于此类情况或编译器错误消息的在线讨论 .

我可以't think of any other way to implement this. Note that in cases where the type parameter can be determined, it'不是问题 . 例如, let prependZero : int -> IObservable<int> -> IObservable<int> = fun n o -> o.StartWith(n) 工作正常 . 但通用版本会很好 .

2 回答

  • 1

    它看起来像围绕通用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)
    
  • 3

    您不需要将单个 value 包装成单个元素数组,以使其与 Observable.StartWith 的最后 ParamArray 参数匹配,只需标量值即可(these samples可能有助于理解原因) .

    但是 value 的泛型类型会在 Observable.StartWith 的两个可用重载之间产生歧义 . 可以通过强制three-agrument overload来实现消歧,方法是将两个参数重载的隐式类型 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 .

    Update

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

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

相关问题