I still cannot figure out why F# compiler cannot infer the type in the following example (taken from the Programming F# 3.0
book):
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
The explanation in the book (page 62) is:
This is because type inference processes code from left-to-right and top-to-bottom, so it sees the lambda passed to Array.map before it sees the type of the array elements passed. (Therefore, the type of the lambda’s parameter is unknown.)
which is reasonable in this case (for fileInfos
, type of file
is inferred as string
since the constructor FileInfo
has its parameter as string
; for fileSizes
, there's no such information).
But I still have doubts because if the explanation is correct, then the type inference (a variant of Hindley–Milner's algorithm W) is so limited. Indeed, there's another source says:
... [F#] ... type inference works top-down, bottom-up, front-to-back, back-to-front, middle-out, anywhere there is type information, it will be used.
Edit: thanks to all for answers, I just add below some details to explain why I still get confused.
In the case of fileSizes
, the compiler knows:
filesInfo : FileInfo []
,Array.map : ('a -> 'b) -> 'a [] -> 'b []
,
it can replaces 'a
by FileInfo
, so there must be info : FileInfo
in the lambda fun info -> info.Length
I can give an example where the type inference of F# shows that it's more capable than "left-to-right, top-to-bottom":
// type inference ok: justF : int [] -> (int -> int -> 'a) -> 'a []
let justF (nums : int []) f =
Array.map (fun x -> f x x) nums
where the compiler infers correctly the type of f : int -> int -> 'a
(obviously it cannot have such an inference if it looks only into the lambda).