我正在编写一个基本上实现状态monad 的计算表达式,我正在尝试使用for
表达式。
我可以使用样板函数forLoop
甚至MBuilder.For()
,它们都会返回一个很好的M<seq<'U>, _>
,可以通过进一步的let!
表达式处理它。但是,当我尝试对for
表达式执行相同操作时,它无法编译,告诉我for
中的表达式必须返回unit
。
我很抱歉我无法缩小代码块。
type M<'T, 'E> = 'T * 'E // Monadic type is a simple tuple
type MFunc<'T, 'U, 'E> = 'T -> M<'U, 'E> // A function producing monadic value
// typical boilerplate functions
let bind (x: M<'T, 'E>) (f: MFunc<'T, 'U, 'E>) : M<'U, 'E> =
let a, s = x
let b, s1 = f a
b, s1 + s
let combine (e1: M<'T, 'E>) (e2: M<'U, 'E>) : M<'U, 'E> = bind e1 (fun _ -> e2)
let delay f = (fun () -> f())()
// These two are explained below
let combineList (e1: M<'T, 'E>) (e2: M<'T seq, 'E>) : M<'T seq, 'E> =
bind
e1
(fun x1 ->
let e2body, e2state = e2
seq{yield! e2body; yield x1}, e2state
)
let forLoop (xs: seq<'T>) (f: MFunc<'T, 'U, 'E>) : M<seq<'U>, 'E> =
Seq.fold
(fun s x -> combineList (f x) s)
(Seq.empty<'U>, 0)
xs
// Builder class
type MBuilder() =
member this.Bind (x: M<'T, 'E>, f: MFunc<'T, 'U, 'E>) : M<'U, 'E> = bind x f
member this.Return(a) = a, 0
member this.Combine(e1,e2) = combine e1 e2
member this.Delay(f) = delay f
member this.Zero() = (), 0
member this.For (xs: seq<'T>, f: MFunc<'T, 'U, 'E> ) : M<seq<'U>, 'E> = forLoop xs f
let stateful = new MBuilder()
let mTest = stateful {
// below is the typical use, just for example
let! var1 = "q", 3
let! var2 = true, 4
// so far so good, the monad returns ("test", 7)
return "test"
}
现在,我正在尝试使用循环。以下三个调用按预期工作,将状态递增的次数与myList
中的元素一样多。它们也会返回一个漂亮的string seq
,显然除了最后一个电话:
let myList = ["one"; "two"; "three"] // define test data
let! var3 = stateful.For(myList, (fun x -> x, 1))
let! var4 = forLoop myList (fun x -> x, 1)
// No return value, as expected
for str in myList do
let! _ = str, 1
return ""
但以下内容无法编译:{{1}}
error FS0001: This expression was expected to have type M<'a,int> but here has type unit
所以我的问题是 - 我做错了什么?
我对 let! var5 =
for str in myList do
let! _ = str, 1
return ""
描述here的两个重载以及如何利用这两个重载感到有些困惑。
答案 0 :(得分:11)
您尝试编写的代码不是语法上有效的计算表达式。语法不允许e
中的表达式let! v = e
中的计算表达式构造。
如果要使用嵌套计算表达式,则必须编写:
let mtest = stateful {
let! var5 =
stateful { for str in myList do
let! _ = str, 1
return "" }
return "something here" }
这应该回答你当前的问题,但有很多事情我觉得你的定义很混乱:
For
的类型令人困惑。它应该是seq<'T> -> ('T -> M<'R>) -> M<'R>
(如果你的monad可以组合多个结果)或seq<'T> -> ('T -> M<unit>) -> M<unit>
(如果你的计算只返回一个值)
您有时在结果中使用seq<'T>
M<_>
(For
),但有时您的monad只返回一个值。你应该在任何地方使用相同的monadic类型。
可以使用For
和Zero
来定义Combine
构造。除非你做一些特别的事情,否则这是最好的方式。请参阅example in the F# specification。
如果您想要更详细的文档,请查看at this article,其中介绍了各种选项。