Я до сих пор не могу понять, почему компилятор F # не может определить тип в следующем примере (взятом из книги Programming F# 3.0
):
open System.IO
// type inference ok: fileInfos : string [] -> FileInfo []
let fileInfos (files : string[]) =
Array.map (fun file -> new FileInfo(file)) files
// type inference does not work !?
let fileSizes (fileInfos : FileInfo []) =
Array.map (fun info -> info.Length) fileInfos
Объяснение в книге (стр. 62):
Это связано с тем, что вывод типа обрабатывает код слева направо и сверху вниз, поэтому он видит лямбду, переданную в Array.map, прежде чем он увидит тип переданных элементов массива.(Следовательно, тип параметра лямбды неизвестен.)
, что в данном случае разумно (для fileInfos
тип file
выводится как string
, поскольку конструктор FileInfo
имеет параметр string
; для fileSizes
такой информации нет).
Но у меня все еще есть сомнения, потому что, если объяснение верное, то вывод типа (вариант алгоритма Хиндли – Милнера)Ж) так ограничен.Действительно, есть еще один источник , который говорит:
... [F #] ... вывод типа работает сверху вниз, снизу вверх, спереди назад, сзадивперед, в середине, везде, где есть информация о типе, она будет использоваться.
Редактировать: спасибо всем за ответы, я просто добавляю ниже некоторые детали, чтобы объяснить, почему я все ещезапутаться.
В случае fileSizes
компилятор знает:
filesInfo : FileInfo []
, Array.map : ('a -> 'b) -> 'a [] -> 'b []
,
он может заменить 'a
на FileInfo
, поэтому в лямбде должно быть info : FileInfo
fun info -> info.Length
Я могу привести пример, когда вывод типа F # показывает, что этоболее способный, чем «слева направо, сверху вниз»:
// type inference ok: justF : int [] -> (int -> int -> 'a) -> 'a []
let justF (nums : int []) f =
Array.map (fun x -> f x x) nums
, где компилятор правильно выводит тип f : int -> int -> 'a
(очевидно, он не может иметь такой вывод, если он смотрит только налямбда).