I want to add support to my Computation Expression builder for this construct:
let f =
foo {
let! x =
foo {
return 1
}
and! y =
foo {
return 2
}
return x + y
}
The compiler says I must implement either Bind2 or MergeSource. However, I cannot find them in the docs.
What signatures should these have? Can you give a simple example?
Here's a simple builder that works with your example:
type Foo<'t> = MkFoo of 't
type FooBuilder() =
member _.Bind(MkFoo x, f) = f x
member _.Return(x) = MkFoo x
member _.MergeSources(MkFoo x, MkFoo y) = MkFoo (x, y)
let foo = FooBuilder()
let f =
foo {
let! x = foo { return 1 }
and! y = foo { return 2 }
return x + y
}
printfn "%A" f // output: MkFoo 3
The purpose of MergeSources is to create a tuple of results that will be automatically de-tupled by the compiler. You might also find this example and this SO question useful.
Related
Consider a generic container like:
type Foo<'t> =
{
Foo : 't
}
This generic function works OK:
module Foo =
let inline emptyFoo () =
{
Foo = LanguagePrimitives.GenericZero
}
But this value does not:
module Foo =
let emptyFoo =
{
Foo = LanguagePrimitives.GenericZero
}
This is because the compiler infers emptyFoo to have type Foo<obj>.
However, the standard library has generic values like List.empty, so how is that achieved there?
You have to make it explicitly generic.
List.empty is implemented like this:
let empty<'T> = ([ ] : 'T list)
You could implement a List by yourself, with an empty. It looks like this.
type L<'a> =
| Null
| Cons of 'a * L<'a>
module L =
let empty = Null
let xs = Cons(1, Cons(2, L.empty))
let ys = Cons(1.0,Cons(2.0,L.empty))
So, why does L.empty in this case works in a generic way? Because the value Null has no special value attached to it. You could say, it is compatible with every other generic.
In your Record on the other hand, you always must produce a value. LanguagePrimitive.GenericZero is not some Generic value. It help so to resolve to a special zero value, and this value is determined by the other code you write.
For example
let x = LanguagePrimitives.GenericZero
is also obj
let x = LanguagePrimitives.GenericZero + 1
will be int. And
let x = LanguagePrimitives.GenericZero + 1.0
will be float. So in some case you can think of GenericZero just as a placeholder for zero for the special type you need, but the code needs to determine at this point, which type you want.
You could change your type, with an option to provide a real empty.
type Foo<'t> = {
Foo: 't option
}
module Foo =
let empty = { Foo = None }
let x = Foo.empty // Foo<'a>
let y = { x with Foo = Some 1 } // Foo<int>
let z = { x with Foo = Some 1.0 } // Foo<float>
Zero Member
Maybe you want a Zero Member on some types. For example
type Vector3 = {X:float; Y:float; Z:float} with
static member create x y z = {X=x; Y=y; Z=z}
static member (+) (a,b) = Vector3.create (a.X + b.X) (a.Y + b.Y) (a.Z + b.Z)
static member Zero = Vector3.create 0.0 0.0 0.0
static member DivideByInt(a,b) =
Vector3.create
(LanguagePrimitives.DivideByInt a.X b)
(LanguagePrimitives.DivideByInt a.Y b)
(LanguagePrimitives.DivideByInt a.Z b)
then you can write
let xs = [1;2;3]
let ys = [Vector3.create 1.0 1.0 1.0; Vector3.create 1.0 1.0 1.0]
let inline sum xs =
List.fold (fun a b -> a + b) LanguagePrimitives.GenericZero xs
let sumx = sum xs // int: 6
let sumy = sum ys // Vector3: 2.0 2.0 2.0
type Identity<'T> = Identity of 'T
type IdentityBuilder() =
member __.Bind (Identity x) (k : 'a -> Identity<'b>) = k x
member __.Return x = Identity x
let identity = new IdentityBuilder()
let three = Identity 3
let four = Identity 4
let twelve =
identity.Bind three <| fun t ->
identity.Bind four <| fun f ->
identity.Return (t * f)
let twelve2 = identity {
let! t = three
let! f = four
return t * f
}
twelve does not introduce any problem, but twelve2 gives
FS0001: This expression was expected to have type
'Identity<'a>' but here has type
''b * 'c'
on the line let! t = three.
I thought twelve and twelve2 should be equivalent... Was I mistaken?
As noted in the comment by Szer, you need to use tupled parameters for the Computation Builder methods. However, it is often convenient to use the curried versions for pipelining, as in your example. Therefore, what I usually do is create a module that contains all the functions required for the Computation Builder in curried form, and then use them in the builder itself. That way I can use either the computation expression syntax or the pipelining syntax depending on the scenario.
In your case, that would look something like this:
type Identity<'T> = Identity of 'T
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Identity =
let bind (f: 'T -> Identity<'U>) (Identity x) = f x
let create x = Identity x
type IdentityBuilder() =
member __.Bind (x, f) = Identity.bind f x
member __.Return x = Identity.create x
let identity = new IdentityBuilder()
let three = Identity 3
let four = Identity 4
let twelve =
three |> Identity.bind (fun t ->
four |> Identity.bind (fun f ->
Identity.create (t * f)))
let twelve2 = identity {
let! t = three
let! f = four
return t * f
}
Another common practice is to define an operator >>= for the bind function so that you can streamline the syntax even more:
let (>>=) f x = Identity.bind x f
let twelve3 = three >>= (fun t -> four >>= (fun f -> Identity.create (t * f)))
I'm trying to write a computational workflow which would allow a computation which can produce side effects like log or sleep and a return value
A usage example would be something like this
let add x y =
compute {
do! log (sprintf "add: x = %d, y= %d" x y)
do! sleep 1000
let r = x + y
do! log (sprintf "add: result= %d" r)
return r
}
...
let result = run (add 100 1000)
and I would like the side effects to be produced when executeComputation is called.
My attempt is
type Effect =
| Log of string
| Sleep of int
type Computation<'t> = Computation of Lazy<'t * Effect list>
let private bind (u : 'u, effs : Effect list)
(f : 'u -> 'v * Effect list)
: ('v * Effect list) =
let v, newEffs = f u
let allEffects = List.append effs newEffs
v, allEffects
type ComputeBuilder() =
member this.Zero() = lazy ((), [])
member this.Return(x) = x, []
member this.ReturnFrom(Computation f) = f.Force()
member this.Bind(x, f) = bind x f
member this.Delay(funcToDelay) = funcToDelay
member this.Run(funcToRun) = Computation (lazy funcToRun())
let compute = new ComputeBuilder()
let log msg = (), [Log msg]
let sleep ms = (), [Sleep ms]
let run (Computation x) = x.Force()
...but the compiler complains about the let! lines in the following code:
let x =
compute {
let! a = add 10 20
let! b = add 11 2000
return a + b
}
Error FS0001: This expression was expected to have type
'a * Effect list
but here has type
Computation<'b> (FS0001)
Any suggestions?
The main thing that is not right with your definition is that some of the members of the computation builder use your Computation<'T> type and some of the other members use directly a pair of value and list of effects.
To make it type check, you need to be consistent. The following version uses Computation<'T> everywhere - have a look at the type signature of Bind, for example:
let private bind (Computation c) f : Computation<_> =
Computation(Lazy.Create(fun () ->
let u, effs = c.Value
let (Computation(c2)) = f u
let v, newEffs = c2.Value
let allEffects = List.append effs newEffs
v, allEffects))
type ComputeBuilder() =
member this.Zero() = Computation(lazy ((), []))
member this.Return(x) = Computation(lazy (x, []))
member this.ReturnFrom(c) = c
member this.Bind(x, f) = bind x f
member this.Delay(funcToDelay:_ -> Computation<_>) =
Computation(Lazy.Create(fun () ->
let (Computation(r)) = funcToDelay()
r.Value))
Here is Explanation of Monad laws in Haskell.
How do explain Monad laws in F#?
bind (M, return) is equivalent to M.
bind ((return x), f) is equivalent to f x.
bind (bind (m, f),g) is equivalent to bind(m, (fun x -> bind (f x, g))).
I think that a good way to understand them in F# is to look at what they mean using the computation expression syntax. I'll write m for some computation builder, but you can imagine that this is async or any other computation type.
Left identity
m { let! x' = m { return x } = m { let x' = x
return! f x' } return! f x' }
Right identity
m { let! x = comp = m { return! comp }
return x }
Associativity
m { let! x = comp = m { let! y = m { let! x = comp
let! y = f x return! f x }
return! g y } return! g y }
The laws essentially tell you that you should be able to refactor one version of the program to the other without changing the meaning - just like you can refactor ordinary F# programs.
Is there an option (maybe) wokflow (monad) in the standrd F# library?
I've found a dozen of hand-made implementations (1, 2) of this workflow, but I don't really want to introduce non-standard and not very trusted code into my project. And all imaginable queries to google and msdn gave me no clue where to find it.
There's no standard computation builder for options, but if you don't need things like laziness (as added in the examples you linked) the code is straightforward enough that there's no reason not to trust it (particularly given the suggestively named Option.bind function from the standard library). Here's a fairly minimal example:
type OptionBuilder() =
member x.Bind(v,f) = Option.bind f v
member x.Return v = Some v
member x.ReturnFrom o = o
member x.Zero () = None
let opt = OptionBuilder()
There's no Maybe monad in the standard F# library. You may want to look at FSharpx, a F# extension written by highly-qualified members of F# community, which has quite a number of useful monads.
I've create an opensource library FSharp.Interop.NullOptAble available on nuget.
It not only works as an option workflow, but it works as a null or nullable workflow as well.
let x = Nullable(3)
let y = Nullable(3)
option {
let! x' = x
let! y' = y
return (x' + y')
} (* |> should equal (Some 6) *)
Works just as well as
let x = Some(3)
let y = Some(3)
option {
let! x' = x
let! y' = y
return (x' + y')
} (* |> should equal (Some 6) *)
Or even
let x = "Hello "
let y = "World"
option {
let! x' = x
let! y' = y
return (x' + y')
} (* |> should equal (Some "Hello World") *)
And if something is null or None
let x = "Hello "
let y:string = null
option {
let! x' = x
let! y' = y
return (x' + y')
} (* |> should equal None *)
Finally if you have a lot of nullable type things, I have a cexpr for chooseSeq {} and if you yield! something null/None it just doesn't get yielded.
See more examples here.