使用FSharpPlus的Reader monad transformer示例

3

我正在尝试理解阅读器(Reader)单子变换器。我正在使用 FSharpPlus,并尝试编译以下示例,其中首先从阅读器环境中读取一些内容,然后执行一些异步计算,最后将两个结果组合起来:

open FSharpPlus
open FSharpPlus.Data

let sampleReader = monad {
    let! value = ask
    return value * 2
}

let sampleWorkflow = monad {
    do! Async.Sleep 5000
    return 4
}

let doWork = monad {
    let! envValue = sampleReader
    let! workValue = liftAsync sampleWorkflow
    return envValue + workValue
}

ReaderT.run doWork 3 |> Async.RunSynchronously |> printfn "Result: %d"

这样做会使我在代码行let! value = ask处遇到编译错误,错误信息对我来说毫无用处,如下所示:

应用默认类型'obj'给类型推断变量时出现类型约束不匹配。没有与方法'op_GreaterGreaterEquals'匹配的重载。

已知返回类型:Async

已知类型参数:< obj,(int -> Async) >

感觉像是我漏掉了某个运算符,但我想不出来是哪个。

1个回答

5
你的代码是正确的,但 F# 类型推断在这种情况下并不那么聪明。
如果你给 sampleReader 添加一个类型注解,它就可以编译成功:
let sampleReader : ReaderT<int,Async<_>> = monad {
    let! value = ask
    return value * 2
}

// val sampleReader : FSharpPlus.Data.ReaderT<int,Async<int>> =
//  ReaderT <fun:sampleReader@7>

更新:

在阅读了您的评论后,如果您想要使其通用化,首先必须声明函数为inline,否则无法应用类型约束:

let inline sampleReader = monad ...

但这就带来了第二个问题:常量无法声明为内联(实际上有一种方法,但太复杂了),只有函数才行。

因此,最简单的方法是将其作为函数:

let inline sampleReader () = monad ...

现在又遇到了第三个问题:代码不能编译 :)

同样,在调用时,您可以给类型推断提供一个最小的提示,只需说明您期望ReaderT<_,_>即可:

let inline sampleReader () = monad {
    let! value = ask
    return value * 2
}

let sampleWorkflow = monad {
    do! Async.Sleep 5000
    return 4
}

let doWork = monad {
    let! envValue = sampleReader () : ReaderT<_,_>
    let! workValue = liftAsync sampleWorkflow
    return envValue + workValue
}

ReaderT.run doWork 3 |> Async.RunSynchronously |> printfn "Result: %d"
结论: 在F#中,定义一个通用函数并不是一项微不足道的任务。 如果你看一下F#+源代码,就会知道我的意思。
在运行示例后,您将看到生成的所有约束条件,并且可能会注意到通过将函数设置为内联和通用来增加编译时间。
这些都表明我们正在推动F#类型系统的极限。
虽然F#+定义了一些可直接使用的通用函数,并且有时这些函数可以组合在一起以创建自己的通用函数,但这不是该库的目标。我是说,您可以这样做,但那就是您自己的事情,在某些探索性开发场景下这可能是有意义的。

哦,很好,足够好,谢谢。我想知道为什么我必须包括 Async<_>sampleReader 只能与 async 一起使用吗?我的意思是为什么它不能同时适用于 Async<_>Option<_>?这似乎是 FSharpPlus 的限制,对吧? - Johannes Egger
哦,我明白了。我刚刚更新了答案。希望这能有所帮助。 - Gus
1
非常好,感谢详细的更新。虽然总体来说不如我所希望的令人满意,但我很高兴知道在F#中它已经达到了最好的水平。 - Johannes Egger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接