Почему F # не может выводить типы, как это делает C # - PullRequest
3 голосов
/ 19 августа 2011

Из книги Томаса Петричека следующий код не работает, так как компилятор не может определить тип параметра 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.

Логика может быть следующей:

  1. подпись Option.map является map : ('T -> 'U) -> 'T option -> 'U option
  2. тип последнего параметра: DateTime option
  3. так что наше map использование выглядит как map : ('T -> 'U) -> 'DateTime option -> 'U option
  4. затем компилятор может попытаться заменить DateTime на 'T, чтобы посмотреть, будет ли это правильно, поэтому у нас есть (DateTime -> 'U) -> 'DateTime option -> 'U option
  5. тогда он может вывести тип 'U, взглянув на тело лямбда-функции, поэтому 'U становится int
  6. и мы наконец имеем (DateTime -> int) -> 'DateTime option -> 'int option

Так почему же F # не может сделать этот вывод? Томас упоминает в своей книге, что F # выводит типы, переходя от первого к последнему аргументу, и поэтому порядок аргументов имеет значение. И именно поэтому F # не может вывести типы в первом примере. Но почему F # не может вести себя как C #, то есть пытаться выводить типы постепенно, начиная с того, что известно?

В большинстве случаев F # гораздо эффективнее, когда речь идет о выводе типов ... поэтому я немного запутался.

Ответы [ 2 ]

4 голосов
/ 27 декабря 2012

Так почему F # не может сделать этот вывод?

F # может сделать это, как это делает OCaml. Недостатком такого рода более сложного вывода является запутывание сообщений об ошибках. OCaml научил нас тому, что в результате возникли такие непонятные ошибки, что на практике вы всегда прибегаете к аннотированию типов, чтобы предотвратить ведение компилятором пути пути вывода типа. Следовательно, было мало мотивации для реализации этого в F #, потому что OCaml уже показал, что это не очень прагматично.

Например, если вы делаете это в OCaml, но неправильно пишете имя метода, то в более поздний момент в коде вы получите огромное сообщение об ошибке, где два выведенных типа классов не совпадают, и вам придется искать его, несоответствие, а затем выполните поиск по своему коду, чтобы найти фактическое местоположение ошибки.

ИМО, классы типов Хаскелла также страдают от аналогичной практической проблемы.

0 голосов
/ 19 августа 2011

F # может делать все, что может сделать вывод типа C # ... и многое, многое другое. AFAIK, степень вывода типа C # автоматически вводит переменную на основе правой части присваивания.

var x = new Dictionary<string, int>();

Эквивалент F # будет:

let x = Dictionary()

или

let x = Dictionary<_,_>()

или

let x = Dictionary<string,_>()

или

let x = Dictionary<string,int>()

Вы можете предоставить столько информации о типе, сколько вам нужно, но вы почти никогда не объявите тип x. Таким образом, даже в этом простом случае, вывод типа F #, очевидно, гораздо более мощный. Вывод типа Хиндли-Милнера печатает целые программы, объединяя все используемые выражения. Насколько я могу судить, вывод типа C # ограничен одним выражением, присваиванием при этом.

...