2010-11-17 11 views
11

Próbuję wywołać metodę .NET akceptującą generyczne IEnumerable<T> z F # przy użyciu seq<U> tak, że U jest podklasą T. To nie działa tak, jak się spodziewałem by:F # i kowariancja interfejsu: co zrobić? (szczególnie seq <> aka IEnumerable <>)

z następującym prostym drukarki:

let printEm (os: seq<obj>) = 
    for o in os do 
     o.ToString() |> printfn "%s" 

są to wyniki uzyskać:

Seq.singleton "Hello World" |> printEm // error FS0001; 
//Expected seq<string> -> 'a but given seq<string> -> unit 

Seq.singleton "Hello World" :> seq<obj> |> printEm // error FS0193; 
//seq<string> incompatible with seq<obj> 

Seq.singleton "Hello World" :?> seq<obj> |> printEm // works! 

Seq.singleton 42 :> seq<obj> |> printEm // error FS0193 
Seq.singleton 42 :?> seq<obj> |> printEm // runtime InvalidCastException! 
//Unable to cast object of type '[email protected][System.Int32]' 
// to type 'System.Collections.Generic.IEnumerable`1[System.Object]'. 

Idealnie chciałbym pierwszy składni do pracy - lub coś w pobliżu to jak po ssible, z sprawdzaniem typu kompilacji. Nie rozumiem, gdzie kompilator znajduje funkcję seq<string> -> unit w tej linii, ale najwyraźniej kowariancja dla IEnumerable nie działa i że w jakiś sposób powoduje to komunikat o błędzie. Użycie jawnego rzutowania daje uzasadniony komunikat o błędzie - ale to też nie działa. Używanie rzutowania działa - ale tylko dla łańcuchów, ints kończy się niepowodzeniem z wyjątkiem (nieprzyjemnym).

Próbuję współpracować z innym kodem .NET; dlatego potrzebuję konkretnych typów IEnumerable.

Jaki jest najczystszy i najskuteczniejszy sposób przesyłania współosiowych lub sprzecznych interfejsów, takich jak IEnumerable w F #?

+1

Jak mówi Desco, najczystszym rozwiązaniem jest zmienić (lub usunąć) deklarację typu na 'os' (jeśli to możliwe). W przypadku niepowiązanej notki, 'o.ToString |> printfn"% s "' może być napisane bardziej zwięźle jako 'o |> printfn"% O "'. – kvb

+0

@kvb Myślę, że @Eamon nie ma problemu z funkcją 'printfn'. –

Odpowiedz

8

Użyj do tego celu Seq.cast. Na przykład:

Seq.singleton "Hello World" |> Seq.cast |> printEm 

Trzeba przyznać, że rezygnuje bezpieczeństwa typ:

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! 

ale jest to celowe.

Alternatywnie, można użyć 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 

który jest po prostu skrótem rodzajową "forall typów 'a when 'a :> Dog".

I na koniec zawsze można zmapować upcast, np.

let printEm (os: seq<obj>) = 
    for o in os do 
     o.ToString() |> printfn "%s" 

Seq.singleton "Hello" |> Seq.map box |> printEm // ok 

gdzie box upcasts do obj.

+4

Jest powolny, nie statycznie sprawdzany typowo ... naprawdę nie to, na co liczyłem - ale działa i jest zwięzły! –

9

Niestety F # doesn; t wsparcie co \ contravariance. Dlatego ten

Seq.singleton "Hello World" :> seq<obj> |> printEm 

nie działa

Można zadeklarować parametr jako SEK < _> lub ograniczenie zestaw typów parametrów do jakiejś konkretnej rodziny za pomocą flexible type ów (z krzyżyka #) to będzie rozwiązać ten scenariusz:

let printEm (os: seq<_>) = 
for o in os do 
    o.ToString() |> printfn "%s" 

Seq.singleton "Hello World" |> printEm 

biorąc pod uwagę to linie:

Seq.singleton 42 :> seq<obj> |> printEm // error FS0193 
Seq.singleton 42 :?> seq<obj> |> printEm 

Odchylenie działa tylko dla klas, więc podobny kod również nie zadziała w C#.

Można spróbować odlewania elementów sekwencyjnych do wymaganego typu jawnie poprzez Seq.cast

+0

Jednak w języku C# odpowiednik 'Seq.singleton" Hello World "|> printEm' zadziała i zostanie sprawdzony statycznie - co jest dla mnie ciekawszym przypadkiem. –

Powiązane problemy