Reputation: 419
Why does this statement give me a type mismatch error,
let x = List.rev [] in (3::x, true::x);;
while this statement does not?
let x = [] in (3::x, true::x);;
I'm assuming it is because x is given a function call in the first statement, while it is only give an empty list in the second statement. But, I am not sure exactly why the second works and the first does not? Any help will be much appreciated. Thanks!
Upvotes: 2
Views: 85
Reputation: 36688
Try the following:
let x = [] ;;
Result: val x : 'a list
. F# knows that x
is a list of as-yet unknown type. If it had any contents, its type would be known. This works perfectly well.
But the following will not work:
let x = List.rev [] ;;
Result:
error FS0030: Value restriction. The value 'x' has been inferred to have generic type
val x : '_a list
Either define 'x' as a simple data term, make it a function with explicit arguments or, if you do not intend for it to be generic, add a type annotation.
The "value restriction" error in F# can be hard to understand — why is []
allowed when List.rev []
is not? — but this article goes into some details. Essentially, F# can always feel safe making functions generic, but it can only feel safe making values generic if the following two conditions apply:
let
is a pure value, e.g. it has no side-effects,
andlet
is immutable.When the expression is []
, then F# knows that it's a pure, immutable value, so it can make it generic ('a list
). But when the expression is someFunction []
, the F# compiler doesn't know what someFunction
is going to do. Even though in this case, we know that List.rev
is part of the standard library and matches those two scenarios, the F# compiler can't know that. In a completely pure language like Haskell, you can know from a function's type signature that it's pure, but F# is a more pragmatic language. So F# takes the guaranteed-safe approach and does not make the result of List.rev []
generic.
Therefore, when you write let x = [] in (3::x, true::x)
, the []
value is a generic empty list, so it can become either an int list
or a bool list
as needed. But when you write let x = List.rev [] in (3::x, true::x)
, F# cannot make List.rev []
generic. It can say "This is a list of a type I don't yet know", and wait for the type to become clear. Then the first expression of a specific type that uses this list, in this case 3::x
, will "lock" the type of that list. I.e., F# cannot consider this list to be generic, so now it has figured out that this empty list is an empty list of int
s. And then when you try to append a bool
to an empty int list
, that's an error.
If you flipped the tuple around so that it was true::x, 3::x
then the type error would "flip" as well: it would expect a bool list
and find an int list
.
So the short version of this answer is: you're hitting the F# value restriction, even though that's not immediately obvious since the error you got didn't mention value restriction at all.
See Understanding F# Value Restriction Errors for a good discussion of the value restriction, including the most common place where you'd normally see it (partially-applied functions).
Upvotes: 3