F#"​​有状态"计算表达式

Cli*_*int 3 f# functional-programming computation-expression

我正在学习F#并且遇到一些绊脚石; 我认为很多是学习功能性思考.

我目前正在学习的一件事是计算表达式,我希望能够定义一个处理某些跟踪状态的计算表达式,例如:

let myOptions = optionListBuilder {
    let! opt1 = {name="a";value=10}
    let! opt2 = {name="b";value=12}
}
Run Code Online (Sandbox Code Playgroud)

我希望能够拥有它,这样myOptions是一个Option<'T> list,所以每个let!绑定操作有效地使建设者"跟踪"的定义选项,它会沿着.

我不想使用可变状态来执行此操作 - 例如,使用由构建器维护的列表并使用每个bind调用进行更新.

有没有办法让它成为可能?


更新:结果Option<'T> list类型只是代表性的,实际上我可能有一个OptionGroup<'T>类型来包含列表以及一些其他信息 - 正如Daniel在下面提到的,我可以使用列表理解来获得一个简单的列表.

N_A*_*N_A 5

我写了一个字符串生成器计算表达式这里.

open System.Text

type StringBuilderUnion =
| Builder of StringBuilder
| StringItem of string

let build sb =
    sb.ToString()

type StringBuilderCE () =
    member __.Yield (txt : string) = StringItem(txt)
    member __.Yield (c : char) = StringItem(c.ToString())
    member __.Combine(f,g) = Builder(match f,g with
                                     | Builder(F),   Builder(G)   ->F.Append(G.ToString())
                                     | Builder(F),   StringItem(G)->F.Append(G)
                                     | StringItem(F),Builder(G)   ->G.Append(F)
                                     | StringItem(F),StringItem(G)->StringBuilder(F).Append(G))
    member __.Delay f = f()
    member __.Zero () = StringItem("")
    member __.For (xs : 'a seq, f : 'a -> StringBuilderUnion) =
                    let sb = StringBuilder()
                    for item in xs do
                        match f item with
                        | StringItem(s)-> sb.Append(s)|>ignore
                        | Builder(b)-> sb.Append(b.ToString())|>ignore
                    Builder(sb)

let builder1 = new StringBuilderCE ()
Run Code Online (Sandbox Code Playgroud)

注意到底层类型是不可变的(包含StringBuilder是可变的,但它不一定是).每个产量组合当前状态和输入输入,而不是更新现有数据,从而产生新的实例.StringBuilderUnion 您可以使用F#列表执行此操作,因为向列表头部添加元素仅仅是构建新值而不是而不是改变现有的价值观.

使用StringBuilderCE如下所示:

//Create a function which builds a string from an list of bytes
let bytes2hex (bytes : byte []) =
    string {
        for byte in bytes -> sprintf "%02x" byte
    } |> build

//builds a string from four strings
string {
        yield "one"
        yield "two"
        yield "three"
        yield "four"
    } |> build
Run Code Online (Sandbox Code Playgroud)

注意到yield而不是let!因为我实际上并不想使用计算表达式中的值.