From the book by Tomas Petricek the following code doesn't work as compiler is unable to infer the type of the dt
parameter:
> Option.map (fun dt -> dt.Year) (Some(DateTime.Now));;
error FS0072: Lookup on object of indeterminate type.
And if we specify type explicitly everything works fine:
> Option.map (fun (dt:DateTime) -> dt.Year) (Some(DateTime.Now));;
val it : int option = Some(2008)
Or we can use pipelining operator to "help" compiler to infer type:
> Some(DateTime.Now) |> Option.map (fun dt -> dt.Year);;
val it : int option = Some(2008)
The question is why F# compiler can't infer the type of the dt
parameter? In this particular case it looks quite easy to infer the dt
's type.
The logic behind it can be the following:
- the signature of
Option.map
ismap : ('T -> 'U) -> 'T option -> 'U option
- the type of the last parameter is
DateTime option
- so our
map
usage looks likemap : ('T -> 'U) -> 'DateTime option -> 'U option
- the compiler then can try to substitute
DateTime
as'T
to see if it would be correct, so we have(DateTime -> 'U) -> 'DateTime option -> 'U option
- then it can infer the
'U
type by looking at the body of the lambda-function, so the'U
becomesint
- and we finally have
(DateTime -> int) -> 'DateTime option -> 'int option
So why F# can't do this inference? Tomas mentions in his book that F# infers types by going from the first to the last argument and that's why the order of arguments matters. And that's why F# can't infer the types in the first example. But why F# can't behave like C#, i.e. try to infer types incrementally starting with what is known?
In most cases F# is much more powerful when speaking about type inference... that't why I'm confused a bit.