F#嵌套的泛型类型与实现的类型不兼容

snickers10分钟

背景:

在F#程序中给出以下两个声明:

  • 类型A实现接口Wrapped<int>
  • 类型B实现接口Wrapped<A>

我所知,我们说typeA与兼容,typeWrapped<int>B兼容Wrapped<A>-意味着A可以将an传递给需要a的函数Wrapped<int>

问题:

根据我的编程经验,鉴于以上两个陈述,我希望以下内容同样适用:

  • 类型B应与类型兼容Wrapped<Wrapped<int>>

因为B具有应A作为类型参数Wrapped<int>,并且AWrapped<int>兼容。

不是这种情况。以下实现:

type Wrapper<'a> = abstract member Value : 'a

type A =
    | A of int

    interface Wrapper<int> with member this.Value = (let (A num) = this in num)

type B =
    | B of A

    interface Wrapper<A> with member this.Value = (let (B a) = this in a)


let f (x:Wrapper<Wrapper<int>>) =
    x.Value.Value

let testValue = f (B (A 1))

B (A 1)声明时出现编译错误

类型B与类型不兼容Wrapper<Wrapper<int>>

题:

由于我能够从逻辑上进行兼容性提升,因此在实现此功能时我做错了什么吗?还是F#不具有此“嵌套兼容性”功能,如果是这种情况,是否有特定原因不具有此功能?


有一个解决方法:

type B =
    | B of A

    interface Wrapper<Wrapper<int>> with member this.Value = (let (B a) = this in a :> Wrapper<int>)

这样会消除编译错误,尽管感觉有些错误。我问自己:“如果我曾经编写过一个处理Wrapper<A>类型的函数怎么办?(如果我添加了更多的Wrapper<A>实现者)

直到

您要查询的功能是协变类型。

协方差允许返回类型是子类型,而不是由泛型类型参数精确定义的返回类型(不是这仅适用于接口,不适用于具体类型)。这样您就可以将转换IEnumerable<string> :?> IEnumerable<object>string :?> object

其他.NET语言也可以声明。这是您在C#中的示例:

interface Wrapper<out T> { }
class A : Wrapper<int> { }
class B : Wrapper<A> { }          

var b = new B();
Action<Wrapper<Wrapper<int>>> unwrap = _ => { };
unwrap(b); //compiles

F#不提供对声明协变类型的支持,也不在没有显式声明的情况下强制类型。其原因主要是协方差导致降级的类型推断。

灵活类型可以在F#中进行协方差这是F#seq中定义为类型的示例IEnumerable<out T>

let s = [1..10] 
let r =  s |> Seq.map(fun _ -> s)

let print1 (v: seq<seq<int>>) = printfn "%A" v
let print2 (v: seq<#seq<_>>) = printfn "%A" v

print1 r //does not compile
print2 r //compiles

如果将通用参数标记为协变并使用了灵活的类型,则有可能使这项工作有效。您可以在C#中使用接口声明,并在F#中引用程序集。

也有mausch / VariantInterfaces根据命名约定修改程序集,以添加协变/反变量声明,因此,如果将类型声明放在单独的程序集中,则可以在构建后运行它。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章