假设我有以下代码:
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#的类型检查器对类型为
A
和B
的对象满意。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#不支持泛型中的接口协方差,因此您只能在obj
:let 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()) ]