Из книги Томаса Петричека следующий код не работает, так как компилятор не может определить тип параметра dt
:
> Option.map (fun dt -> dt.Year) (Some(DateTime.Now));;
error FS0072: Lookup on object of indeterminate type.
И если мы явно указываем тип, все работает нормально:
> Option.map (fun (dt:DateTime) -> dt.Year) (Some(DateTime.Now));;
val it : int option = Some(2008)
Или мы можем использовать оператор конвейерной передачи, чтобы «помочь» компилятору определить тип:
> Some(DateTime.Now) |> Option.map (fun dt -> dt.Year);;
val it : int option = Some(2008)
Вопрос в том, почему компилятор F # не может определить тип параметра dt
? В данном конкретном случае довольно просто вывести тип dt
.
Логика может быть следующей:
- подпись
Option.map
является map : ('T -> 'U) -> 'T option -> 'U option
- тип последнего параметра:
DateTime option
- так что наше
map
использование выглядит как map : ('T -> 'U) -> 'DateTime option -> 'U option
- затем компилятор может попытаться заменить
DateTime
на 'T
, чтобы посмотреть, будет ли это правильно, поэтому у нас есть (DateTime -> 'U) -> 'DateTime option -> 'U option
- тогда он может вывести тип
'U
, взглянув на тело лямбда-функции, поэтому 'U
становится int
- и мы наконец имеем
(DateTime -> int) -> 'DateTime option -> 'int option
Так почему же F # не может сделать этот вывод? Томас упоминает в своей книге, что F # выводит типы, переходя от первого к последнему аргументу, и поэтому порядок аргументов имеет значение. И именно поэтому F # не может вывести типы в первом примере. Но почему F # не может вести себя как C #, то есть пытаться выводить типы постепенно, начиная с того, что известно?
В большинстве случаев F # гораздо эффективнее, когда речь идет о выводе типов ... поэтому я немного запутался.