【问题标题】:Why does this computation expression builder expect "unit" in my for loop?为什么这个计算表达式生成器在我的 for 循环中期望“单位”?
【发布时间】:2014-04-17 22:49:37
【问题描述】:

这是this question 的后续问题。

我正在尝试创建一个计算表达式构建器,该构建器通过自定义操作累积值,同时还支持标准 F# 语言构造。为了讨论一个简单的例子,我使用了一个构建 F# 列表的计算表达式。感谢 kvb 和 Daniel 的建议,我走得更远了,但仍然遇到for 循环问题。

建造者:

type Items<'a> = Items of 'a list

type ListBuilder() =
    member x.Yield(vars) = Items [], vars
    member x.Run(l,_) = l
    member x.Zero() = Items [], ()
    member x.Delay f = f()
    member x.ReturnFrom f = f

    member x.Combine((Items curLeft, _), (Items curRight, vars)) =
        (Items (curLeft @ curRight), vars)

    member x.Bind(m: Items<'a> * 'v, f: 'v -> Items<'a> * 'o) : Items<'a> * 'o =
        let (Items current, vals) = m
        x.Combine(m, f vals)

    member x.While(guard, body) =
        if not (guard()) then
            x.Zero()
        else
            x.Bind(body, fun () -> x.While(guard, body))

    member x.TryWith(body, handler) =
        try
            x.ReturnFrom(body())
        with e ->
            handler e

    member x.TryFinally(body, compensation) =
        try
            x.ReturnFrom(body())
        finally
            compensation()

    member x.Using(disposable:#System.IDisposable, body) =
        let body' = fun() -> body disposable
        x.TryFinally(body', fun () ->
            match disposable with
            | null -> ()
            | disp -> disp.Dispose())

    member x.For(xs:seq<'a>, body) =
        x.Using(xs.GetEnumerator(), fun enum ->
            x.While(enum.MoveNext, x.Delay(fun () -> body enum.Current)))

    [<CustomOperation("add", MaintainsVariableSpace=true)>]
    member x.Add((Items current, vars), [<ProjectionParameter>] f) =
        Items (current @ [f vars]), vars

    [<CustomOperation("addMany", MaintainsVariableSpace=true)>]
    member x.AddMany((Items current, vars), [<ProjectionParameter>] f) =
        Items (current @ f vars), vars


let listBuilder = ListBuilder()

let build (Items items) = items

这个版本允许我以前做不到的事情,例如:

let stuff = 
    listBuilder {
        let x = 5 * 47
        printfn "hey"
        add x
        addMany [x .. x + 10]
    } |> build

但是,我仍然收到编译器错误:

let stuff2 =
    listBuilder {
        for x in 1 .. 50 do
            add x            
    } |> build

在这种情况下,IDE 会在 for x in 中的 x 下划线并告诉我,“这个表达式应该有类型 unit,但这里有 int 类型。”

我不太清楚为什么它期望循环变量是单元类型。显然我在某处得到了错误的方法签名,并且我怀疑我没有在我应该在的每个地方都通过我的累积状态,但是编译器错误并没有帮助我缩小我出错的地方。任何建议将不胜感激。

【问题讨论】:

    标签: f# computation-expression


    【解决方案1】:

    直接原因是您的While 函数限制了body 的类型。但是,一般来说,您不能在同一个计算表达式中同时使用自定义操作和控制流操作符,所以我认为即使您修复了签名,您也永远无法完全按照您的意愿行事。

    【讨论】:

    • 好的,这就解释了为什么我在混合控制流运算符和自定义操作时遇到了这么多麻烦。我将添加一些额外的自定义运算符来处理集合 - 这样缺少循环不会成为太大问题。谢谢。
    猜你喜欢
    • 2015-11-29
    • 1970-01-01
    • 2021-01-12
    • 2012-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-24
    相关资源
    最近更新 更多