Error on Extension Methods when Inlining - f#

I want to extend some system types and later use them via inlining
type System.String with
member this.foo n = this + "!" + n
type System.Boolean with
member this.foo n = sprintf "%A!%A" this n
Now I call these extension methods
let x = "foo".foo "bar"
let y = true.foo "bar"
which gives me this
- val x : System.String = "foobar"
- val y : string = "true!"bar""
All fine and dandy - but now I want to wrap the call to .foo into an inline function
let inline foo n v = (^T : (member foo : ^N -> ^S) v, n)
let z = foo "bar" "baz"
Only now I get a compiler error telling me that
> The type 'string' does not support the operator 'foo':
well ... it does!
Can somebody explain whats going on?

Extension methods are not taken into account in static member constraints (possible duplicate of this) and this is a general problem when you want to implement generic code using member constraints and make it work also with already defined or primitive types.
See the user voice request, also the workarounds mentioned here and Don Syme's explanation of why it's complicated to implement it in the F# compiler.
If you follow the links there you will see currently the way to workaround it basically involves creating an intermediate type and overloads for all the known types and a generic one for the extensions.
This is a very basic example of how to workaround it:
type Foo = Foo with
static member ($) (Foo, this:int) = fun (n:int) -> this + n
static member ($) (Foo, this:string) = fun n -> this + "!" + n
static member ($) (Foo, this:bool) = fun n -> sprintf "%A!%A" this n
let inline foo this n = (Foo $ this) n
//Now you can create your own types with its implementation of ($) Foo.
type MyType() =
static member ($) (Foo, this) =
fun n -> printfn "You called foo on MyType with n = %A" n; MyType()
let x = foo "hello" "world"
let y = foo true "world"
let z = foo (MyType()) "world"
You can enhance it by adding an explicit generic overload for new types:
// define the extensions
type System.String with
member this.foo n = this + "!" + n
type System.Boolean with
member this.foo n = sprintf "%A!%A" this n
// Once finished with the extensions put them in a class
// where the first overload should be the generic version.
type Foo = Foo with
static member inline ($) (Foo, this) = fun n -> (^T : (member foo : ^N -> ^S) this, n)
static member ($) (Foo, this:string) = fun n -> this.foo n
static member ($) (Foo, this:bool) = fun n -> this.foo n
// Add other overloads
static member ($) (Foo, this:int) = fun n -> this + n
let inline foo this n = (Foo $ this) n
//later you can define any type with foo
type MyType() =
member this.foo n = printfn "You called foo on MyType with n = %A" n; MyType()
// and everything will work
let x = foo "hello" "world"
let y = foo true "world"
let z = foo (MyType()) "world"
You can further refine it by writing the static constraints by hand and using a member instead of an operator (see an example here),
At the end of the day you will end up with something like this generic append function from FsControl.

Statically resolved type constraints do not support extension methods. It's just not a feature of F#.
If you would like F# to gain support for higher-kinded polymorphism, you can vote for it on user voice.

Related

Higher order functions with generic argument in F#

RE: What is the best way to pass generic function that resolves to multiple types
Please read the referenced link before going further below
I am trying to extend the concept and pass a generic function that takes 2 parameters and does something with them.
The static approach works, however the interface based one causes a compile error (see the code lines marked with //error):
The declared type parameter '?' cannot be used here since the type parameter cannot be resolved at compile time.
Does anyone know how to fix it?
module MyModule
type T = Content of int
with
static member (+) ((Content i1), (Content i2)) = Content (i1 + i2)
static member (*) ((Content i1), (Content i2)) = Content (i1 * i2)
type W = { Content: int }
with
static member (+) ({Content = i1}, {Content = i2}) = { Content = i1 + i2 }
static member (*) ({Content = i1}, {Content = i2}) = { Content = i1 * i2 }
type Sum = Sum with static member inline ($) (Sum, (x, y)) = x + y
type Mul = Mul with static member inline ($) (Mul, (x, y)) = x * y
let inline f1 (la: 'a list) (lb: 'b list) reducer =
let a = la |> List.reduce (fun x y -> reducer $ (x, y))
let b = lb |> List.reduce (fun x y -> reducer $ (x, y))
(a, b)
type I = abstract member Reduce<'a> : 'a -> 'a -> 'a
let f2 (la: 'a list) (lb: 'b list) (reducer: I) =
let a = la |> List.reduce reducer.Reduce
let b = lb |> List.reduce reducer.Reduce
(a, b)
let main ()=
let lt = [Content 2; Content 4]
let lw = [{ Content = 2 }; { Content = 4 }]
let _ = f1 lt lw Sum
let _ = f1 lt lw Mul
let _ = f2 lt lw { new I with member __.Reduce x y = x + y} //error
let _ = f2 lt lw { new I with member __.Reduce x y = x * y} //error
0
The problem with your attempt is that you can't use operators + or * on parameters x and y, because it's not known that their type 'a has those operators defined.
To answer your further question in comments about how to achieve it anyway - if you want to use multiplication and addition on any type 'a that the caller chooses, you have to specify that. For an interface method, the only way to do this is by constraining the type parameter 'a, and the only two kinds of constraints that .NET runtime supports are "has a parameterless constructor" and "implements a given interface or inherits from a given class".
The latter one would be useful in your case: make both types implement the interface and then constrain type parameter 'a to implement that interface:
type IArithmetic<'a> =
abstract member add : 'a -> 'a
abstract member mult : 'a -> 'a
type T = Content of int
with
interface IArithmetic<T> with
member this.add (Content y) = let (Content x) = this in Content (x + y)
member this.mult (Content y) = let (Content x) = this in Content (x * y)
type W = { Content: int }
with
interface IArithmetic<W> with
member this.add y = { Content = this.Content + y.Content }
member this.mult y = { Content = this.Content * y.Content }
type I = abstract member Reduce<'a when 'a :> IArithmetic<'a>> : 'a -> 'a -> 'a
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
// the constraint right here
...
let _ = f2 lt lw { new I with member __.Reduce x y = x.add y }
let _ = f2 lt lw { new I with member __.Reduce x y = x.mult y }
Is this a bit awkward? I guess so, but you're kind of doing the same thing for the SRTP version, so why not?
The core idea is: if you want your Reduce method to work not with just any type, but only with types that can do certain things, you have to specify what those things are. In the SRTP case you're doing that by defining the (+) and (*) operators. In the interface case you're doing that by implementing the interface.
Q: But can I make the interface somehow pick up the (+) and (*) operators?
A: In general, no. The .NET runtime just doesn't support the kind of constraints like "any type that has a method with certain signature". This means that such constraints can't be compiled down to IL, which means they can't be used in an interface implementation.
And this is the price you pay for using SRTPs: all those inline functions - they don't get compiled to IL, they always get expanded (inserted, substituted) at use sites. For small, simple functions, this is no big deal. But if your whole program is like that, you might see some unexpected compiled code bloat, potentially translating to slower startup time etc.
Having said all that, I must note that the code you're showing is toy POC kind of code, not intended to solve any real, practical problem. And as such, most musings on it are in danger of being completely useless.
If you have an actual problem in mind, perhaps try sharing it, and somebody would suggest the best solution for that specific case.
In particular, I have a nagging feeling that you might not actually need higher-rank functions (that's what it's called when a function doesn't lose genericity when passed as parameter).

Chaining infix operators with statically resolved type parameter

I'm trying to create an infix operator to make System.Text.StringBuilder slightly easier to use.
I have the following inline function using statically resolved type parameters:
let inline append value builder = (^T : (member Append : _ -> ^T) (builder, value))
which handles all the overloads of StringBuilder.Append. This works fine as a regular function:
StringBuilder()
|> append 1
|> append " hello "
|> append 2m
|> string
// Result is: '1 hello 2'
When I try to use define an infix operator like so:
let inline (<<) builder value = append value builder
it works when all parameters in a chain are of the same type:
StringBuilder()
<< 1
<< 2
<< 3
|> string
// Result is: '123'
but fails with parameters of different types:
StringBuilder()
<< 1
<< "2" // <- Syntax error, expected type 'int' but got 'string'.
<< 123m // <- Syntax error, expected type 'int' but got 'decimal'.
The expected type seems to be inferred by the first usage of the << operator in the chain. I would assume that each << would be applied separately.
If the chain is split into separate steps the compiler is happy again:
let b0 = StringBuilder()
let b1 = b0 << 1
let b2 = b1 << "2"
let b3 = b2 << 123m
b3 |> string
// Result is: '12123'
Is it possible to create such an operator?
Edit
A hacky "solution" seems to be to pipe intermediate results through the identity function whenever the type of the argument changes:
StringBuilder()
<< 1 // No piping needed here due to same type (int)
<< 2 |> id
<< "A" |> id
<< 123m
|> string
// Result is: '12A123'
This is quite odd - and I would say it may be a compiler bug. The fact that you can fix this by splitting the pipeline into separate let bindings is what makes me think this is a bug. In fact:
// The following does not work
(StringBuilder() << "A") << 1
// But the following does work
(let x = StringBuilder() << "A" in x) << 1
I think the compiler is somehow not able to figure out that the result is again just StringBuilder, which can have other Append members. A very hacky version of your operator would be:
let inline (<<) builder value =
append value builder |> unbox<StringBuilder>
This performs an unsafe cast to StringBuilder so that the return type is always StringBuilder. This makes your code work (and it chooses the right Append overlaods), but it also lets you write code that uses Append on non-StringBuilder things and this code will fail at runtime.
I can possibly add a data point to this mystery, albeit I am
only able to surmise that this behavior might have something to do with the particular overload for value: obj. If I uncomment that line and try to run it, the compiler says:
Script1.fsx(21,14): error FS0001: Type mismatch. Expecting a
'a -> 'c
but given a
System.Text.StringBuilder -> System.Text.StringBuilder
The type ''a' does not match the type 'System.Text.StringBuilder'
This happened while trying to map the various overloads of System.Text.StringBuilder to statically resolved type parameters on an operator. This seems to be a fairly standard technique in similar cases, since it will produce compile-time errors for unsupported types.
open System.Text
type Foo = Foo with
static member ($) (Foo, x : bool) = fun (b : StringBuilder) -> b.Append x
static member ($) (Foo, x : byte) = fun (b : StringBuilder) -> b.Append x
static member ($) (Foo, x : char[]) = fun (b : StringBuilder) -> b.Append x
static member ($) (Foo, x : char) = fun (b : StringBuilder) -> b.Append x
static member ($) (Foo, x : decimal) = fun (b : StringBuilder) -> b.Append x
static member ($) (Foo, x : float) = fun (b : StringBuilder) -> b.Append x
static member ($) (Foo, x : float32) = fun (b : StringBuilder) -> b.Append x
static member ($) (Foo, x : int16) = fun (b : StringBuilder) -> b.Append x
static member ($) (Foo, x : int32) = fun (b : StringBuilder) -> b.Append x
static member ($) (Foo, x : int64) = fun (b : StringBuilder) -> b.Append x
// static member ($) (Foo, x : obj) = fun (b : StringBuilder) -> b.Append x
static member ($) (Foo, x : sbyte) = fun (b : StringBuilder) -> b.Append x
static member ($) (Foo, x : string) = fun (b : StringBuilder) -> b.Append x
static member ($) (Foo, x : uint16) = fun (b : StringBuilder) -> b.Append x
static member ($) (Foo, x : uint32) = fun (b : StringBuilder) -> b.Append x
static member ($) (Foo, x : uint64) = fun (b : StringBuilder) -> b.Append x
let inline (<.<) b a =
(Foo $ a) b
// val inline ( <.< ) :
// b:'a -> a: ^b -> 'c
// when (Foo or ^b) : (static member ( $ ) : Foo * ^b -> 'a -> 'c)
let res =
StringBuilder()
<.< 1
<.< 2
<.< 3
<.< "af"
<.< 2.32m
|> string
// val res : string = "123af2,32"
I think there is a solution in the following:
let inline append value builder = (^T: (member Append: _ -> ^S) (builder, value))
let inline (<<) builder value = append value builder
let builder = new StringBuilder()
let result =
builder
<< 1
<< " hello "
<< 2m
|> string
printfn "%s" result
As seen the return value from Append is set to ^S instead of ^T and ^S is resolved to require Append as member.
It will find the correct overload for Append which you can see, it you use the following mockup of a StringBuilder:
type MyStringBuilder() =
member this.Append(value: int) =
printfn "int: %d" value;
this
member this.Append(value: string) =
printfn "string: %s" value;
this
member this.Append(value: decimal) =
printfn "decimal: %f" value;
this
member this.Append(value: obj) =
printfn "obj: %A" value
this
let builder = new MyStringBuilder()
let result =
builder
<< 1
<< " hello "
<< 2m
|> string
Warning: There is though a peculiarity in the following setup:
let builder = StringBuilder()
let result =
builder
<< 1
<< " hello "
<< 2m
<< box " XX "
|> string
when compiling this with the extra << box " XX " the compiler gets lost somewhere in the process and is rather long time to compile (only when using StringBuilder() - not MyStringBuilder()) and the intellisense and coloring etc. seems to disappear - in my Visual Studio 2019 as least.
At first, I thought it has something to do with the box value, but it rather seems to have something to do with the number of chained values???
The below works:
let inline (<<) (builder:StringBuilder) (value:'T) = builder.Append(value)
let x = StringBuilder()
<< 1
<< 2
<< 3
<< "af"
<< 2.32m
|> string
I think you need to be specific about the StringBuilder type otherwise it will pick only one of the overloads.

How to constrain type parameter must be an algebraic type (int, float, BigInteger, BigRational, ...)

-- While there are some questions on the net wrt. type constraints already, I didn't find one that can help me solving my issue. --
Goal: I want to create my own Vector/Matrix types, but so, that the implementation does not lock in to a speicific BigRational (or alike) type. All I'd prefer to require is the standard algebraic operations on such types (+ - * / % equality).
open System
type Foo<'T> (value: 'T) =
member inline __.Value : 'T = value
static member inline Add (a: Foo<'T>) (b: Foo<'T>) =
Foo<'T>(a.Value + b.Value)
module Foo =
let inline Create (v) = Foo(v)
let log (foo: #Foo<_>) =
printfn "Foo: %s" (foo.Value.ToString())
[<EntryPoint>]
let main argv =
Foo.log (Foo.Create("hi ho"))
Foo.log (Foo<int>(31415))
Foo.log (Foo<float>(3.1415))
Foo.log (Foo<int>.Add (Foo.Create(3)) (Foo.Create(4)))
let a = Foo.Create(13)
let b = Foo.Create(3.1415)
Foo.log (Foo<int>.Add (a.Value) (a.Value))
Foo.log (Foo<float>.Add (b.Value) (b.Value))
0 // return an integer exit code
I cannot get this tiny example code to compile for more than one single type, such as Foo<int> as well as Foo<float>. How could I do it right?
Many thanks in advance,
Christian.
You almost have it, actually.
In order to create a function that accepts any type that has a + operator, this function must have statically-resolved type parameters (SRTP). For this, it must be inline, which your Add is so that's ok. However here Add is not a generic method: it's a method on the generic type Foo<'T>, so it receives its 'T parameter from it. And a type cannot have SRTP.
A simple fix is to move Add from being a method on the type Foo<'T> to being a function in the module Foo. Then it will become actually generic.
open System
type Foo<'T> (value: 'T) =
member inline __.Value : 'T = value
module Foo =
let inline Create (v) = Foo(v)
let inline Add (a: Foo< ^T>) (b: Foo< ^T>) =
Foo< ^T>(a.Value + b.Value)
let log (foo: #Foo<_>) =
printfn "Foo: %s" (foo.Value.ToString())
[<EntryPoint>]
let main argv =
Foo.log (Foo.Create("hi ho"))
Foo.log (Foo<int>(31415))
Foo.log (Foo<float>(3.1415))
Foo.log (Foo.Add (Foo.Create(3)) (Foo.Create(4)))
let a = Foo.Create(13)
let b = Foo.Create(3.1415)
Foo.log (Foo.Add a a)
Foo.log (Foo.Add b b)
0
I think all you need is the inline keyword if you just want to propagate the member constraints of F#'s overloaded arithmetic operators.
type Foo<'T> (value : 'T) =
member __.Value = value
static member inline (+) (a : Foo<_>, b : Foo<_>) = Foo(a.Value + b.Value)
// static member
// ( + ) : a:Foo< ^a> * b:Foo< ^b> -> Foo< ^c>
// when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^c)
Now you can add two Foos of the same type that support a static member (+), Foo 3 + Foo 4 or Foo 3.14 + Foo 3.14, even Foo(Foo 3) + Foo(Foo 4); but not Foo 3 + Foo 3.14. You may still instantiate types that do not possess such member.

Erroneous FS0064 warning? This construct causes code to be less generic than indicated by the type annotations

I am trying to make a class which implements a generic .NET IComparer interface for use with the various System.Collections.Generic collections. This IComparer will map the collection's values into an ordering key using an externally provided function.
An example application might be a SortedSet<City> which sorts by population, but where the population is dynamically retrieved from an external data source which is separately maintained and updated.
open System
open System.Collections.Generic
type ExternalComparer<'T>(compareBy: ('T -> 'U) when 'U :> IComparable) =
let compareBy = compareBy
interface IComparer<'T> with
member this.Compare(a, b) =
let x = compareBy a
let y = compareBy b
if x < y then -1 else if x > y then 1 else 0
The compiler issues the following warning on the if x < y expression, specifically on the x:
Warning FS0064 This construct causes code to be less generic than
indicated by the type annotations. The type variable 'U has been
constrained to be type 'IComparable'.
In fact, I intend the type variable 'U to be constrained to be the type 'IComparable,' and I tried to express that intent with the when 'U :> IComparable constraint in the definition of the compareBy function type.
Is this warning message erroneous, or am I doing something wrong?
I think your solution - just accept a function of type 'T -> IComparable is the right thing to do, even if it means that the users of your ExternalComparer might need to insert an upcast to make the code compile.
To explain why you were getting the error, your class has only one generic type parameter 'T and the 'U parameter is missing there - so the compiler constrained it to IComparable. If you wanted to fix that, you'd have to add another generic parameter:
type ExternalComparer<'T, 'U when 'U :> IComparable>(compareBy: ('T -> 'U)) =
let compareBy = compareBy
interface IComparer<'T> with
member this.Compare(a, b) =
let x = compareBy a :> IComparable
let y = compareBy b :> IComparable
if x < y then -1 else if x > y then 1 else 0
Additionally, I also had to add :> IComparable so that we convert the results from whatever 'U is to IComparable that can be compared. This adds another generic parameter, which is quite silly.
If you wanted to avoid that, you could use a static member that converts a 'T -> 'U function to
'T -> IComparable function before passing it to the constructor:
type ExternalComparer<'T>(compareBy: ('T -> IComparable)) =
let compareBy = compareBy
interface IComparer<'T> with
member this.Compare(a, b) =
let x = compareBy a
let y = compareBy b
if x < y then -1 else if x > y then 1 else 0
static member Create(compareBy : 'T -> #IComparable) =
ExternalComparer(fun v -> compareBy v :> IComparable)
Now you can use the Create method nicely:
ExternalComparer<string>(fun v -> int v :> _) // Requires upcast
ExternalComparer<string>.Create(fun v -> int v) // Works directly!
The problem is that you're defining the compareBy type to return a 'U, constrained to IComparable, when you could more clearly have defined it to return an IComparable directly, like this:
type ExternalComparer<'T>(compareBy: ('T -> IComparable)) =
Since you know 'U will always be an IComparable, the use of a generic type is incorrect.
It seems to me you could collapse a chunk of the code into an object expression:
let bigCity = {Name="NYC"; population = 1_000_000}
let smallCity = {Name="KC"; population = 1000}
let cityComparer compFunc = { new System.Collections.Generic.IComparer<'T> with
override __.Compare(c1, c2) =
let x = compFunc c1
let y = compFunc c2
if x < y then -1 else if x > y then 1 else 0
}
let comp = cityComparer (fun x -> x.population)
comp.Compare(smallCity,bigCity)
//val it : int = -1

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

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'm having a lot of trouble figuring out how to add this support.
To give a stand-alone example, here's a dead-simple and fairly pointless computation expression that builds F# lists:
type Items<'a> = Items of 'a list
type ListBuilder() =
member x.Yield(()) = Items []
[<CustomOperation("add")>]
member x.Add(Items current, item:'a) =
Items [ yield! current; yield item ]
[<CustomOperation("addMany")>]
member x.AddMany(Items current, items: seq<'a>) =
Items [ yield! current; yield! items ]
let listBuilder = ListBuilder()
let build (Items items) = items
I can use this to build lists just fine:
let stuff =
listBuilder {
add 1
add 5
add 7
addMany [ 1..10 ]
add 42
}
|> build
However, this is a compiler error:
listBuilder {
let x = 5 * 39
add x
}
// This expression was expected to have type unit, but
// here has type int.
And so is this:
listBuilder {
for x = 1 to 50 do
add x
}
// This control construct may only be used if the computation expression builder
// defines a For method.
I've read all the documentation and examples I can find, but there's something I'm just not getting. Every .Bind() or .For() method signature I try just leads to more and more confusing compiler errors. Most of the examples I can find either build up a value as you go along, or allow for regular F# language constructs, but I haven't been able to find one that does both.
If someone could point me in the right direction by showing me how to take this example and add support in the builder for let bindings and for loops (at minimum - using, while and try/catch would be great, but I can probably figure those out if someone gets me started) then I'll be able to gratefully apply the lesson to my actual problem.
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 [<ProjectionParameter>] 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
[<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
member x.Run(l,_) = l
The most complete examples I've seen are in §6.3.10 of the spec, especially this one:
/// Computations that can cooperatively yield by returning a continuation
type Eventually<'T> =
| Done of 'T
| NotYetDone of (unit -> Eventually<'T>)
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Eventually =
/// The bind for the computations. Stitch 'k' on to the end of the computation.
/// Note combinators like this are usually written in the reverse way,
/// for example,
/// e |> bind k
let rec bind k e =
match e with
| Done x -> NotYetDone (fun () -> k x)
| NotYetDone work -> NotYetDone (fun () -> bind k (work()))
/// The return for the computations.
let result x = Done x
type OkOrException<'T> =
| Ok of 'T
| Exception of System.Exception
/// The catch for the computations. Stitch try/with throughout
/// the computation and return the overall result as an OkOrException.
let rec catch e =
match e with
| Done x -> result (Ok x)
| NotYetDone work ->
NotYetDone (fun () ->
let res = try Ok(work()) with | e -> Exception e
match res with
| Ok cont -> catch cont // note, a tailcall
| Exception e -> result (Exception e))
/// The delay operator.
let delay f = NotYetDone (fun () -> f())
/// The stepping action for the computations.
let step c =
match c with
| Done _ -> c
| NotYetDone f -> f ()
// The rest of the operations are boilerplate.
/// The tryFinally operator.
/// This is boilerplate in terms of "result", "catch" and "bind".
let tryFinally e compensation =
catch (e)
|> bind (fun res -> compensation();
match res with
| Ok v -> result v
| Exception e -> raise e)
/// The tryWith operator.
/// This is boilerplate in terms of "result", "catch" and "bind".
let tryWith e handler =
catch e
|> bind (function Ok v -> result v | Exception e -> handler e)
/// The whileLoop operator.
/// This is boilerplate in terms of "result" and "bind".
let rec whileLoop gd body =
if gd() then body |> bind (fun v -> whileLoop gd body)
else result ()
/// The sequential composition operator
/// This is boilerplate in terms of "result" and "bind".
let combine e1 e2 =
e1 |> bind (fun () -> e2)
/// The using operator.
let using (resource: #System.IDisposable) f =
tryFinally (f resource) (fun () -> resource.Dispose())
/// The forLoop operator.
/// This is boilerplate in terms of "catch", "result" and "bind".
let forLoop (e:seq<_>) f =
let ie = e.GetEnumerator()
tryFinally (whileLoop (fun () -> ie.MoveNext())
(delay (fun () -> let v = ie.Current in f v)))
(fun () -> ie.Dispose())
// Give the mapping for F# computation expressions.
type EventuallyBuilder() =
member x.Bind(e,k) = Eventually.bind k e
member x.Return(v) = Eventually.result v
member x.ReturnFrom(v) = v
member x.Combine(e1,e2) = Eventually.combine e1 e2
member x.Delay(f) = Eventually.delay f
member x.Zero() = Eventually.result ()
member x.TryWith(e,handler) = Eventually.tryWith e handler
member x.TryFinally(e,compensation) = Eventually.tryFinally e compensation
member x.For(e:seq<_>,f) = Eventually.forLoop e f
member x.Using(resource,e) = Eventually.using resource e
The tutorial at "F# for fun and profit" is first class in this regard.
http://fsharpforfunandprofit.com/posts/computation-expressions-intro/
Following a similar struggle to Joel's (and not finding §6.3.10 of the spec that helpful) my issue with getting the For construct to generate a list came down to getting types to line up properly (no special attributes required). In particular I was slow to realise that For would build a list of lists, and therefore need flattening, despite the best efforts of the compiler to put me right. Examples that I found on the web were always wrappers around seq{}, using the yield keyword, repeated use of which invokes a call to Combine, which does the flattening. In case a concrete example helps, the following excerpt uses for to build a list of integers - my ultimate aim being to create lists of components for rendering in a GUI (with some additional laziness thrown in). Also In depth talk on CE here which elaborates on kvb's points above.
module scratch
type Dispatcher = unit -> unit
type viewElement = int
type lazyViews = Lazy<list<viewElement>>
type ViewElementsBuilder() =
member x.Return(views: lazyViews) : list<viewElement> = views.Value
member x.Yield(v: viewElement) : list<viewElement> = [v]
member x.ReturnFrom(viewElements: list<viewElement>) = viewElements
member x.Zero() = list<viewElement>.Empty
member x.Combine(listA:list<viewElement>, listB: list<viewElement>) = List.concat [listA; listB]
member x.Delay(f) = f()
member x.For(coll:seq<'a>, forBody: 'a -> list<viewElement>) : list<viewElement> =
// seq {for v in coll do yield! f v} |> List.ofSeq
Seq.map forBody coll |> Seq.collect id |> List.ofSeq
let ve = new ViewElementsBuilder()
let makeComponent(m: int, dispatch: Dispatcher) : viewElement = m
let makeComponents() : list<viewElement> = [77; 33]
let makeViewElements() : list<viewElement> =
let model = {| Scores = [33;23;22;43;] |> Seq.ofList; Trainer = "John" |}
let d:Dispatcher = fun() -> () // Does nothing here, but will be used to raise messages from UI
ve {
for score in model.Scores do
yield makeComponent (score, d)
yield makeComponent (score * 100 / 50 , d)
if model.Trainer = "John" then
return lazy
[ makeComponent (12, d)
makeComponent (13, d)
]
else
return lazy
[ makeComponent (14, d)
makeComponent (15, d)
]
yield makeComponent (33, d)
return! makeComponents()
}

Resources