F#计算表达式:如何使用`for`返回`seq`?

byt*_*ter 4 f# computation-expression

我正在编写一个基本上实现状态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"
    }
Run Code Online (Sandbox Code Playgroud)

现在,我正在尝试使用循环.以下三个调用按预期工作,将状态递增的次数与中的元素一样多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 ""
Run Code Online (Sandbox Code Playgroud)

但以下不编译: 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 ""
Run Code Online (Sandbox Code Playgroud)

所以我的问题是 - 我做错了什么?

我也对这里For描述的两个重载以及如何利用这两个重载感到困惑.

Tom*_*cek 11

您尝试编写的代码不是语法上有效的计算表达式.语法不允许在表达式计算表达式结构elet! v = e.

如果要使用嵌套计算表达式,则必须编写:

let mtest = stateful {
  let! var5 = 
    stateful { for str in myList do 
                 let! _ = str, 1 
                 return "" }
  return "something here" }
Run Code Online (Sandbox Code Playgroud)

这应该回答你的直接问题,但有很多事情我觉得你的定义很混乱:

  • 你的类型For令人困惑.它应该是seq<'T> -> ('T -> M<'R>) -> M<'R>(如果你的monad可以组合多个结果)或seq<'T> -> ('T -> M<unit>) -> M<unit>(如果你的计算只返回一个值)

  • 你有时在结果(in )中使用seq<'T>inside ,但有时你的monad只返回一个值.你应该到处使用相同的monadic类型.M<_>For

  • 所述For构建体可以在以下方面进行定义ZeroCombine.除非你做一些特别的事情,否则这是最好的方式.请参阅F#规范中示例.

如果您需要更详细的文档,请查看本文,其中介绍了各种选项.