如何解决如何实现自定义 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 实例有效(支持 IObservable
和 map
),看来 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。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。