So this is my first attempt at implementing a computational expression in F#. I’m not going to go into definitions or the likes as to what computational expressions are as there are far better posts out there on this subject than I could probably write, at this time. I’ll list some in a “further reading” section at the end of the post.
What I’m going to present here is a builder which really just creates a list of names – nothing particularly clever – but it gives a basic idea on getting started with computational expressions (I hope).
So first off we need to create a builder type, mine’s called CurveBuilder
type Items = Items of string list type CurveBuilder() = member this.Yield (()) = Items [] [<CustomOperation ("create", MaintainsVariableSpace = true)>] member this.Create (Items sources, name: string) = Items [ yield! sources yield name ]
As this is just a simple demo, the only thing I’m doing is creating a list of strings which you would be correct in thinking that I could do easier using a collection class, but I’m really just interested in seeing the builder and it’s interactions without too much clutter, so go with me on this one…
Before we discuss the builder code, let’s take a look at how we’d use a builder in our application
let builder = CurveBuilder() let curves = builder { create "risky_curve1.gbp" create "risky_curve2.usd" }
In this code we first create an instance of a CurveBuilder, then when we use the builder to create a list of the curves. The Yield method is first called on the CurveBuilder, returning an empty Items value. Subsequent calls to the create method then call the Create method of the CurveBuilder and, as can be seen, create a new Items value containing the previous Items plus our new curve name.
Simple enough but surely there’s more to this than meets the eye
The example above is a minimal implementation, of course, of a builder. You’ll notice that we have an attribute on one method (the Create method) and not on the Yield method.
So the attribute CustomOperation is used on a member of a builder to create new “query operators”. Basically it extends the builder functionality with new operators named whatever you want to name them.
On the other hand the Yield method is a “standard” builder method. There are several other methods which F# knows about implicitly within a builder class, including Return, Zero, Bind, For, YieldFrom, ReturnFrom, Delay, Combine, Run and more, see Computation Expressions (F#)
for a full list of “built-in workflows”. With these we can obviously produce something more complex than the example presented in this post – which is in essence a glorified “list” builder with snazzy syntax.
Discriminated Union gotcha
One thing I got caught out with from the above code is that I wanted to simply list the curves I’d created but couldn’t figure out how to “deconstruct” the discriminated union
type Items = Items of string list
to a simple string list – at this point I claim ignorance as I’m not using F# as much as I’d like so am still fairly inexperienced with it.
My aim was to produce something like this
for curve in curves do printfn "Curve: %s" curve
but this failed with the error The type ‘Items’ is not a type whose values can be enumerated with this syntax, i.e. is not compatible with either seq<_>, IEnumerable<_> or IEnumerable and does not have a GetEnumerator method
I need to in essence disconnect the Items from the string list, to achieve this I found the excellent post Discriminated Unions.
The solution is as follows
let (Items items) = curves for curve in items do printfn "Curve: %s" curve
Note: the let (Items items) = curves
Further Reading
Implementing a builder: Zero and Yield
Implementing a builder: Combine
Implementing a builder: Delay and Run
Implementing a builder: Overloading
Implementing a builder: Adding laziness
Implementing a builder: The rest of the standard methods