How do I write a computation expression builder that accumulates a value and also allows standard language constructs?

前端 未结 4 1535
悲哀的现实
悲哀的现实 2021-02-03 10:34

I have a computation expression builder that builds up a value as you go, and has many custom operations. However, it does not allow for standard F# language constructs, and I\'

4条回答
  •  醉梦人生
    2021-02-03 10:40

    The best place to look is the spec. For example,

    b {
        let x = e
        op x
    }
    

    gets translated to

       T(let x = e in op x, [], fun v -> v, true)
    => T(op x, {x}, fun v -> let x = e in v, true)
    => [| op x, let x = e in b.Yield(x) |]{x}
    => b.Op(let x = e in in b.Yield(x), x)
    

    So this shows where things have gone wrong, though it doesn't present an obvious solution. Clearly, Yield needs to be generalized since it needs to take arbitrary tuples (based on how many variables are in scope). Perhaps more subtly, it also shows that x is not in scope in the call to add (see that unbound x as the second argument to b.Op?). To allow your custom operators to use bound variables, their arguments need to have the [] attribute (and take functions from arbitrary variables as arguments), and you'll also need to set MaintainsVariableSpace to true if you want bound variables to be available to later operators. This will change the final translation to:

    b.Op(let x = e in b.Yield(x), fun x -> x)
    

    Building up from this, it seems that there's no way to avoid passing the set of bound values along to and from each operation (though I'd love to be proven wrong) - this will require you to add a Run method to strip those values back off at the end. Putting it all together, you'll get a builder which looks like this:

    type ListBuilder() =
        member x.Yield(vars) = Items [],vars
    
        []
        member x.Add((Items current,vars), []f) =
            Items (current @ [f vars]),vars
    
        []
        member x.AddMany((Items current, vars), []f) =
            Items (current @ f vars),vars
    
        member x.Run(l,_) = l
    

提交回复
热议问题