假设我有以下代码:

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和带有trait的Scala中使用了这种模式,因此令我感到惊讶的是,我无法在F#中做到这一点。我在这里想念什么?

最佳答案

在类型参数位置使用_基本上告诉编译器“为我推断类型”。列表中第一个完全定义的类型是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()) ]

10-08 02:36