简体   繁体   English

F# 和自动向上转换:有时会,有时不会,这背后的理由是什么?

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

Lately I played a bit with the possibilities of automatic upcasting in F#, I know that F# is forcing to be explicit, however I've noticed that upcasting actually kinda exists in some cases.最近我在 F# 中尝试了自动向上转换的可能性,我知道 F# 是强制明确的,但是我注意到在某些情况下实际上存在向上转换。

Below is some code showing cases where the upcasting is working and cases where it is not:下面是一些代码,显示了向上转换工作的情况和不工作的情况:

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)]

Additionally, I've also noticed that upcasting tuples individually works but failed instantly when it's a collection:此外,我还注意到向上转换元组单独工作但当它是一个集合时立即失败:

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

I would like to know the rationale behind.我想知道背后的原理。 Imho it looks a bit inconsistent, in some cases you can, in some others you cannot.恕我直言,它看起来有点不一致,在某些情况下你可以,在另一些情况下你不能。 I'm probably missing something out and I would like to know what.我可能错过了一些东西,我想知道是什么。

C# has implicit covariance and contravariance. C# 具有隐式协变和逆变。 This lets you automatically cast IEnumerable<string> to IEnumerable<object> , or use an Action<object> with an int .这使您可以自动将IEnumerable<string>强制转换为IEnumerable<object> ,或者将Action<object>int一起使用。

F# doesn't have either. F# 也没有。 Vote here!在这里投票! . .

So you can't write:所以你不能写:

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

The rest of what you see is type inference trying to choose the best matching type.您看到的 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

Note that all of your fail cases are either for non-homogeneous collections or for covariant collections.请注意,所有失败案例都是针对非同质 collections 或协变 collections。

For now, F# has flexible types, which is essentially syntactic sugar for:目前,F# 有灵活的类型,它本质上是语法糖:

#T = 'a where 'a :> T

It lets you have limited covariance.它让你有有限的协方差。

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

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

I'm sure someone else can provide more background.我相信其他人可以提供更多背景。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM