簡體   English   中英

F# 和自動向上轉換:有時會,有時不會,這背后的理由是什么?

[英]F# and automatic upcasting: sometimes it does, sometimes it doesn't, what is the rationale behind that?

最近我在 F# 中嘗試了自動向上轉換的可能性,我知道 F# 是強制明確的,但是我注意到在某些情況下實際上存在向上轉換。

下面是一些代碼,顯示了向上轉換工作的情況和不工作的情況:

type NameValue(name: string, value: obj) = class end
type GenNameValue<'T>(name: string, value: 'T) = class end
type MoreNameValue<'Name, 'Value>(name: 'Name, value: 'Value) = class end

let takeObjList (source: obj list) = printfn "%A" source
let takeNameValueList (source: NameValue list) = printfn "%A" source
let takeGenNameValueList (source: GenNameValue<obj> list) = printfn "%A" source
let takeMoreNameValueList (source: MoreNameValue<string, obj> list) = printfn "%A" source
let takeAnonymousList (source: {| Name: string; Value: obj |} list) = printfn "%A" source
let takeStructAnonymousList (source: struct {| Name: string; Value: obj |} list) = printfn "%A" source

let takeTupleList (source: (string * obj) list) = printfn "%A" source
let takeStructTupleList (source: struct (string * obj) list) = printfn "%A" source
let takeValueTuples (source: ValueTuple<string, obj> list) = printfn "%A" source
let takeRefTuples (source: Tuple<string, obj> list) = printfn "%A" source

// Implicit upcasting to obj => works
takeObjList [42; "middle"; 16us]
takeNameValueList [NameValue("a", "c"); NameValue("b", 42)]
takeGenNameValueList [GenNameValue<obj>("a", "c"); GenNameValue<obj>("a", 42)]
takeMoreNameValueList [MoreNameValue<string, obj>("a", "c"); MoreNameValue<string, obj>("a", 42)]
takeAnonymousList [{| Name = "a"; Value = "c" |}; {| Name = "b"; Value = 42 |}]
takeStructAnonymousList [{| Name = "a"; Value = "c" |}; {| Name = "b"; Value = 42 |}]

// Implicit upcasting to obj => doesn't work
//  [FS0001] This expression was expected to have type 'obj' but here has type 'string'
//  [FS0001] This expression was expected to have type 'obj' but here has type 'int'
takeTupleList [("a", "c"); ("b", 42)]
takeStructTupleList [("a", "c"); ("b", 42)]
takeValueTuples [ValueTuple.Create("a", "c"); ValueTuple.Create("a", 42)]
takeRefTuples [Tuple.Create("a", "c"); Tuple.Create("a", 42)]

此外,我還注意到向上轉換元組單獨工作但當它是一個集合時立即失敗:

let build1 a b: (string * obj) list = [a; b]
let build2 a: (string * obj) list = a

// Does work
build1 ("a", "c") ("a", 42) |> ignore
// Does not work
build2 [("a", "c"); ("a", 42)] |> ignore

我想知道背后的原理。 恕我直言,它看起來有點不一致,在某些情況下你可以,在另一些情況下你不能。 我可能錯過了一些東西,我想知道是什么。

C# 具有隱式協變和逆變。 這使您可以自動將IEnumerable<string>強制轉換為IEnumerable<object> ,或者將Action<object>int一起使用。

F# 也沒有。 在這里投票! .

所以你不能寫:

[1; 2; 3] :> obj list //compile error

您看到的 rest 是類型推斷,試圖選擇最佳匹配類型。

let printlist (list: obj list) = 
    list |> List.iter(printfn "%A")

printlist [1; 2; 3] //compiles - literals will be inferred using type annotation
let intlist = [1; 2; 3]
printlist intlist //this will not compile

請注意,所有失敗案例都是針對非同質 collections 或協變 collections。

目前,F# 有靈活的類型,它本質上是語法糖:

#T = 'a where 'a :> T

它讓你有有限的協方差。

let printlist (list: #obj list) = 
    list |> List.iter(printfn "%A")

let intlist = [1; 2; 3]
printlist intlist //compiles

我相信其他人可以提供更多背景。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM