fghzxm
fghzxm

Reputation: 1207

Type inference error in computation expression

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?

Upvotes: 2

Views: 82

Answers (1)

Aaron M. Eshbach
Aaron M. Eshbach

Reputation: 6510

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)))

Upvotes: 3

Related Questions