2017-02-17 29 views
3

假设我有以下代码:集合,实现在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" 

我可以让F#的类型检查满意A类型和B的对象列表,只要我明确指定接口类型,ala:

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中,所以我很惊讶我无法在F#中执行此操作。我在这里错过了什么?

回答

4

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

F#不直接支持异构应用的类型参数一样的是:_意味着某些特定的未指定类型,而不是任意数量的未指定类型。如果你迫切希望做类似的事情,那么.NET类型系统中的存在类型会有一种机械但笨拙的编码,但我建议不要这样做,除非有一些特别有吸引力的好处。

另外请注意,即使你可以做你想做的事,你也无法对列表做很多事情 - 你可以在任何给定的元素上执行什么操作?