在F#中实现通用接口的对象的集合

时间:2017-02-17 22:54:20

标签: generics interface f#

假设我有以下代码:

type A =
  abstract member hi: string

type B() =
  interface A with
    member self.hi: string = "Hello"

type C() =
  interface A with
    member self.hi: string = "Yo"

只要我明确指定了接口类型ala:

,我就可以使F#的类型检查器满意AB类型的对象列表。
let l: A list = [ B(); C() ]

但是当通用参数进入图片时,我有点难过。如,

type A<'T> =
  abstract member thing: 'T

type B() =
  interface A<int> with
    member self.thing: int = 1

type C() =
  interface A<string> with
    member self.thing: string = "Yo"

我尝试使用像

这样的东西
let l: A<_> list = [B(); C()]

F#似乎想要顽固地填写泛型类型参数:

error FS0001: The type 'C' is not compatible with the type 'A<int>'

请注意,我在Java中使用了带有标准接口的模式,在Scala中使用了traits,所以我很惊讶我无法在F#中执行此操作。我在这里缺少什么?

2 个答案:

答案 0 :(得分:4)

在类型参数位置使用_基本上告诉编译器&#34;推断我的类型&#34;。列表中第一个完全定义的类型是A<int>,因此_固定为int。您需要自己提供所有列表元素的(最不常见的)超类型。由于F#不支持泛型中的界面协方差,因此您在此处所做的只是objlet l: obj list = [B(); C()]

请注意,对于C#也是如此,因为方差仅适用于引用类型:

interface IInvariant<T>
{
    T Item { get; }
}

interface ICovariant<out T>
{
    T Item { get; }
}

class Foo : IInvariant<int>, ICovariant<int>
{
    public int Item { get; }
}

class Bar : IInvariant<string>, ICovariant<string>
{
    public string Item { get; }
}

class Baz
{
    static void Check()
    {
        var a = new IInvariant<object>[] { new Foo(), new Bar() };
        // CS0266  Cannot implicitly convert type 'Foo' to 'C.IInvariant<object>'
        // CS0266  Cannot implicitly convert type 'Bar' to 'C.IInvariant<object>'

        var b = new ICovariant<object>[] { new Foo(), new Bar() };
        // CS0266  Cannot implicitly convert type 'Foo' to 'C.ICovariant<object>'
    }
}

在F#中,您可以创建一个区分联合来捕获类型信息:

type InvariantWrapper =
| Integer of IInvariant<int>
| Stringy of IInvariant<string>

let c = [ Integer(Foo()); Stringy(Bar()) ]

答案 1 :(得分:4)

F#并不直接支持异类使用类型参数:_表示某些特定的未指定类型,而不是任意数量的未指定类型。如果你非常想要做类似的事情,那么在.NET类型系统中存在一种机械但笨拙的存在类型编码,但除非有一些特别引人注目的好处,否则我建议不要这样做。

另请注意,即使您可以执行您想要的操作,也无法对列表执行任何操作 - 您可以对其中的任何给定元素执行哪些操作?