I define two functions, one with and one without a flexible type:
let concatA = Seq.concat // ... : seq<#seq<'b>> -> seq<'b>
let concatB = concatA : seq<seq<'b> -> seq<'b>
Is there a context in which I can use one but not the other?
My understanding is that flexible types are helpful for functions f: #B -> #B
, where the type guarantees that (f (x : D)) : D
for any D
derived from B
. I don't see how the flexible type is helpful for Seq.concat
?
(I chose Seq.concat
as an example because its used in the documentation for Flexible Types uses it, but using concatB
in that example still seems to type everything.)
It lets you call Seq.concat
with a sequence of specific collections. For example:
[ [1]; [2] ] |> concatA // Works
[ [1]; [2] ] |> concatB // Does not work
The argument is list<list<int>>
which only implements the interface seq<list<int>>
. This means that the second call does not work (because the argument requires seq<seq<int>>
), but the first one does thanks to the flexible type - seq<#seq<int>>
unifies with seq<list<int>>
.
A slightly confusing thing is that if F# knows the required type of a collection literal, it sometimes inserts conversions behind the scenes, so the following would actually work too (but only thanks to a hidden conversion):
concatB [ [1]; [2] ]