Cthutu
Cthutu

Reputation: 8907

How do I make the F# compiler infer my arguments to the generic function?

I have written some test code to wrap my head around the syntax of generics:

let add_stuff<'T> x y =
   printfn "%A" (x + y)

let stuff() = 
    add_stuff 5.5 2.4
    add_stuff 3 4

The 2nd add_stuff call does not compile. Why is this? Why doesn't the compiler infer the types?

Upvotes: 2

Views: 121

Answers (2)

AMieres
AMieres

Reputation: 5004

There are 2 issues with the code:

First, the generic type 'T is not used by any of the parameters. The parameters need to be declared of type 'T like this:

// val add_stuff: 'T -> 'T -> unit
let add_stuff<'T> (x:'T) (y:'T) =
    printfn "%A" (x + y)

This creates a new error message:

The declared type parameter 'T' cannot be used here since the type parameter cannot be resolved at compile time

The issue in this case is the + operator. It uses Statically Resolved Type Parameters which means your function also needs to use them which basically means it needs to be inlined:

// val add_stuff: ^T -> ^T -> unit
let inline add_stuff (x:^T) (y:^T) =
    printfn "%A" (x + y)

Notice the difference in the type signature 'T is a generic type, it can be resolved at runtime. ^T is an SRTP, it needs to be resolved at compile time, thus the inline keyword.

If you had not used the + operator then you would not need to inline it. Notice the difference between pair_stuff and add_stuff:

// val pair_stuff: 'T -> 'W -> unit
let pair_stuff (x:'T) (y:'W) =
    printfn "%A" (x , y)

// val add_stuff:  ^a -> ^b -> unit
let inline add_stuff x y =
    printfn "%A" (x + y)

let stuff() = 
    add_stuff 5.5 2.4
    add_stuff 3 4
    pair_stuff 5.5 2.4
    pair_stuff 3 4

stuff()
// 7.9
// 7
// (5.5, 2.4)
// (3, 4)

Upvotes: 5

Chad Gilbert
Chad Gilbert

Reputation: 36375

You cannot use Generics for this because they are resolved at runtime and will not vary by inlining.

You need to use Statically Resolved Type Parameters which will be resolved at compile time:

let inline add_stuff (x: ^X) (y: ^Y) =
    printfn "%A" (x + y)

Statically resolved type parameters are primarily useful in conjunction with member constraints, which are constraints that allow you to specify that a type argument must have a particular member or members in order to be used. There is no way to create this kind of constraint by using a regular generic type parameter.

Upvotes: 3

Related Questions