Search code examples
f#computation-expression

How to Access a Value from a Builder using Custom Operation in a Computation Expression


I have a Computational Expression Builder which receives a value during construction

type SomeBuilder<'e> (e: 'e) =
    member this.Bind(x, fn) = ...
    member this.Return x  = ...
    member this.ReturnFrom x = ...

let buildSome v = SomeBuilder(v)

buildSome 2 {
    return 1
}

Now I'd like to access the value e from within the Computational Expression via a custom operation so that

buildSome 2 {
    return 1 + e()
}

So I really want to access properties/values in the underlying builder object and work with them

I imagine I would need something like

type SomeBuilder<'e> (e: 'e) =
    member this.Bind(x, fn) = ...
    member this.Return x  = ...
    member this.ReturnFrom x = ...
    [<CustomOperation("e")>]
    member this.E () = e        

but that doesn't work.

So my question is

a) is something like this possible using CustomOperations and Computational Expressions b) and if it is possible, how?

Disclaimer:
As usual in programming there is a million ways to achieve similar effects in completely different ways. I am explicitly asking for this particular way and I am OK if the answer is simply "No". But please refrain from answers that are non answers in the narrowest sense laid out here.


Solution

  • I'm not sure you'll like my answer and whether it's within your boundaries, but you could capture the builder instance using a trick like this:

    type SomeBuilder<'e> (e: 'e) =
        member this.Value = e
    
        [<CustomOperation("extract", MaintainsVariableSpaceUsingBind = true, AllowIntoPattern = true)>]
        member this.Extract (state) = this
    
        member this.Bind(x, fn) = fn x
        member this.Return x  = x
        member this.ReturnFrom x = x
    
    
    let builder e = new SomeBuilder<_>(e)
    
    let x = builder 1 {
        extract into builder // now we've brought builder in the scope
        printfn "here we can read the value = %d" builder.Value
        return 0
    }