F# and interface covariance: what to do? (specifically seq<> aka IEnumerable<>)
Unfortunately F# doesn;t support co\contravariance. That's why this
Seq.singleton "Hello World" :> seq<obj> |> printEm
doesn't work
You can declare parameter as seq<_>, or limit set of parameter types to some specific family by using flexible types (with hash #) this will fix this scenario:
let printEm (os: seq<_>) =
for o in os do
o.ToString() |> printfn "%s"
Seq.singleton "Hello World" |> printEm
Considering this lines:
Seq.singleton 42 :> seq<obj> |> printEm // error FS0193
Seq.singleton 42 :?> seq<obj> |> printEm
Variance only works for classes, so similar code will not work in C# too.
You can try casting sequence elements to required type explicity via Seq.cast
Use Seq.cast
for this. For example:
Seq.singleton "Hello World" |> Seq.cast |> printEm
Admittedly, this gives up type safety:
type Animal() = class end
type Dog() = inherit Animal()
type Beagle() = inherit Dog()
let printEm (os: seq<Dog>) =
for o in os do
o.ToString() |> printfn "%s"
Seq.singleton (Beagle()) |> Seq.cast |> printEm // ok
Seq.singleton (Animal()) |> Seq.cast |> printEm // kaboom!
but it is expedient.
Alternatively, you can use flexible types:
type Animal() = class end
type Dog() = inherit Animal()
type Beagle() = inherit Dog()
let printEm (os: seq<#Dog>) = // note #Dog
for o in os do
o.ToString() |> printfn "%s"
Seq.singleton (Beagle()) |> printEm // ok
Seq.singleton (Animal()) |> printEm // type error
which is just shorthand for the generic "forall types 'a when 'a :> Dog
".
And finally, you can always map the upcast, e.g.
let printEm (os: seq<obj>) =
for o in os do
o.ToString() |> printfn "%s"
Seq.singleton "Hello" |> Seq.map box |> printEm // ok
where box
upcasts to obj
.