Fsharp Pete
Fsharp Pete

Reputation: 751

F# Type inference on Math.Max

Why won't the compiler pick the correct version of an overloaded method if it has inferred the types of the input parameters.

In this example why can't it pick the correct Math.Max to use when the type has correctly inferred on the elements being compared:

let listMax = 
    List.map2 (fun l r -> Math.Max(l,r)) [2;4] [5;3]       //compile error 
let listMax2 = 
    List.map2 (fun (l:int) r -> Math.Max(l,r)) [2;4] [5;3] //no compile error    

Of course you can just use the max function in this case, but there are plenty of other methods that don't have a native equivalent.

Upvotes: 2

Views: 990

Answers (2)

Tomas Petricek
Tomas Petricek

Reputation: 243041

As @Gos correctly points out, using the pipeline operator helps in this case. The reason is that the type inference works from left to right - so, if you use the pipeline operator, it knows that one of the inputs is a list of integers and, based on that, it picks the right overload of Math.Max.

The compiler generally needs to know the type when it does overload resolution or when you want to invoke a member on a object (e.g. if you wanted to do l.Foo() inside the map function) - because in this case, it needs to know what exactly the type is.

F# also defines its own versions of basic math functions which work better with type inference. So you can replace Math.Max with the max functions (which you can also nicely pass directly to map2):

List.map2 max [2;4] [5;3] 

This works better, because F# does not need to perform overload resolution (the function is not overloaded). It just keeps track of a special generic constraint that is satisfied and resolved later.

Upvotes: 7

Goswin Rothenthal
Goswin Rothenthal

Reputation: 2344

I can't tell you why but I found that the pipeline operator often helps type inference:

let listMax = 
    [5;3] |> List.map2 (fun l r -> Math.Max(l,r)) [2;4]

Upvotes: 1

Related Questions