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:
Option.map
is map : ('T -> 'U) -> 'T option -> 'U option
DateTime option
map
usage looks like map : ('T -> 'U) -> 'DateTime option -> 'U option
DateTime
as 'T
to see if it would be correct, so we have (DateTime -> 'U) -> 'DateTime option -> 'U option
'U
type by looking at the body of the lambda-function, so the 'U
becomes int
(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.
So why F# can't do this inference?
F# could do that as OCaml does that. The disadvantage of this kind of more sophisticated inference is the obfuscation of error messages. OCaml taught us that the result generated such incomprehensible errors that, in practice, you always resort to annotating types in order to prevent the compiler from being led down a type inference garden path. Consequently, there was little motivation to implement this in F# because OCaml had already shown that it is not very pragmatic.
For example, if you do that in OCaml but mis-spell the method name then you will get a huge error message at some later point in the code where two inferred class types mismatch and you will have to hunt through it to find the discrepancy and then search back through your code to find the actual location of the error.
IMO, Haskell's type classes also suffer from an equivalent practical problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With