如何实现自定义 Monad 实例,尤其是 FSharpPlus 中的 IObservable?

问题描述

FSharpPlus 提供了 monad CE 和几个 monad 转换器,我想将 ReaderT<'a,IObservable<'b>> 与 FSharpPlus 的 monad CE 一起使用,这需要 {{1} 的 monad 实例的定义}.

所需代码的示例是

IObservable

预计翻译成

let test (x: IObservable<int>) = 
    monad {
        let! a = x
        let! b = x
        return a + b
    }

但是 monad CE 不支持 let test (v: IObservable<int>) = x.SelectMany(fun n -> Observable.Return(n + 1)) 开箱即用,并且在上面的 IObservable 函数的同一模块中添加如下扩展不起作用。

test

如何为 type IObservable<'a> with static member Return (x: 'T) : IObservable<'T> = Observable.Return(x) static member (>>=) (x: IObservable<'T>,f: 'T->IObservable<'U>) : IObservable<'U> = x.SelectMany(f) 类型定义 monad 实例?


更新

更新用例来自

IObservable

为了防止无意中与 Functor 的关系。

更新

正如@Gus 在回答中提到的那样,直接为 IObservable 添加 monad 实例可能并不容易。

经过一番搜索,由于 let test (x: IObservable<int>) = monad { let! n = x return n + 1 } 的 Functor 实例有效(支持 IObservablemap),看来 free monad 可能是一种解决方案。

以下代码似乎有效:

|>>

因此,我们需要做的是在monad CE for open System open System.Reactive.Linq open FSharpPlus open FSharpPlus.Data let rec interpret (p: Free<IObservable<'a>,'a>) : IObservable<'a> = match Free.run p with | Choice1Of2 x -> Observable.Return(x) | Choice2Of2 p' -> p'.SelectMany(interpret) let test = monad { let! a = Free.liftF (Observable.Range(0,4).Select(fun n -> n * 10)) let! b = Free.liftF (Observable.Range(0,10)) return a + b } |> interpret s的所有bind之前添加Free.liftF,并在最后添加一个IObservable辅助函数

interpret 实现可能不是堆栈安全的(或者可能是通过 IObservable.SelectMany 实现?)。

尽管这可以编译并且对于简单的情况似乎可以正确运行,但我想知道这种免费 monad 的用法是否正确?

解决方法

在撰写此答案时,尽管 F# 编译器中有 longstanding RFC 和 PR 来实现它,但无法让扩展方法对特征约束可见。

因此,暂时为了向抽象(如 Monad)添加类型,您需要编辑类型的源以添加所需的方法或抽象源。

前者对于 IObservable 基本上是不可能的,我的意思是你可以尝试向他们提交 PR,但后一种选择在短期内更可行:提交 PR 或至少在 F#+ 中打开一个问题将 IObservable 直接添加为 Monad。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...