Liviu
Liviu

Reputation: 605

Why is F# type inference not working in this case

In following case

let a = [1]
let f x = x
let b = a |> List.map f

a is an int list. We call List.map so for sure f must be a int->? function. Why is f still typed as 'a ->'a instead of int->int? If this is how type inference (not)works in F# i see little benefits for it.

Is there an workaround without specifying the type for x in f explicitly? Lets imagine x has type (A*B*C list ( D option, E list)))

Also another interesting thing:

open System.Collections.Generic

let d = new Dictionary()
d.[1] ="a"

This does not work unless the "new" keyword is removed.

Upvotes: 0

Views: 371

Answers (3)

Fsharp Pete
Fsharp Pete

Reputation: 751

To add a little something to the fine answers already listed here.

There is one circumstance where you will want to not use the generic inferred type. The generic version of f will be slower than the type specific version. But F# has a keyword for taking care of that for you:

let inline f x = x

Which tells the compiler to use the type specific version of any operations you apply to x. The inferred type will remain generic. You will only need this in time critical section of code.

As noted elsewhere, the generic inference is a useful thing and you'll probably grow to like it.

Upvotes: 0

Søren Debois
Søren Debois

Reputation: 5688

The function f is typed 'a -> 'a because it works at every type, not just int:

> f "foo";;
val it = "foo" : string
> f 0.8;;
val it = 0.8 : float

This an advantage: the generic type 'a -> 'a gives you the freedom to use f when it is safe to do so, as opposed to only at the type int -> int.

Upvotes: 3

Mark Seemann
Mark Seemann

Reputation: 233457

Type inferencing works just fine here. f is inferred as having the type 'a -> 'a, which means that it'll take a value of the generic type 'a and return a value of the same generic type 'a.

In this case, type inferencing kicks in when you use it. Because a has the type int list, and because List.map has the signature ('T -> 'U) -> 'T list -> 'U list, it can infer types from the expression

let b = a |> List.map f

In this case, the 'T list argument is a, so it has the type int list. This means it now knows that 'T is int.

The function passed to List.map has the general form 'T -> 'U, but in this case we pass f, which has the form 'a -> 'a, which is equivalent to 'T -> 'T. Because of this, the compiler understands that in this case, 'T and 'U are the same types.

This means that it can infer that the return type 'U list must also be int list.

Upvotes: 4

Related Questions