假设我有以下代码:
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中使用了带有标准接口的模式,在Scala中使用了traits,所以我很惊讶我无法在F#中执行此操作。我在这里缺少什么?
答案 0 :(得分:4)
在类型参数位置使用_
基本上告诉编译器&#34;推断我的类型&#34;。列表中第一个完全定义的类型是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()) ]
答案 1 :(得分:4)
F#并不直接支持异类使用类型参数:_
表示某些特定的未指定类型,而不是任意数量的未指定类型。如果你非常想要做类似的事情,那么在.NET类型系统中存在一种机械但笨拙的存在类型编码,但除非有一些特别引人注目的好处,否则我建议不要这样做。
另请注意,即使您可以执行您想要的操作,也无法对列表执行任何操作 - 您可以对其中的任何给定元素执行哪些操作?