Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 354
robkuz Avatar asked Mar 05 '23 08:03

robkuz


2 Answers

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
}
like image 199
Pierre Irrmann Avatar answered Apr 07 '23 06:04

Pierre Irrmann


To show that primary constructor arguments are in scope for the builder's instance methods:

type SomeBuilder<'e> (e: 'e) =
    member __.Bind(x, fn) = fn x
    member __.Return x = x
    [<CustomOperation("e", MaintainsVariableSpaceUsingBind = true,  AllowIntoPattern = true)>]
    member __.E _ = e

SomeBuilder 2 {
    e into i
    return 1 + i }
// val it : int = 3
SomeBuilder "bar" {
    e into s
    return "foo" + s }
// val it : string = "foobar"

Consider the position of the custom operation inside the builder; it will ignore expressions that precede it.

like image 30
kaefer Avatar answered Apr 07 '23 04:04

kaefer