创建辅助非泛型构造函数时,F#中出现编译器错误



我有一个泛型类,默认构造函数接受泛型类型的参数。当创建第二个非泛型构造函数时,F#编译器会抱怨This type parameter has been used in a way that constrains it to always be 'EmptyType',我根本不理解这个错误。为什么会受到限制?第二个构造函数与第一个构造函数的泛型属性有什么关系?

此示例显示了错误(在F#操场中可重复(。注意,只需注释第二个构造函数(第9行(,就可以解决编译问题:

type MyInterface = interface end
type EmptyType() = interface MyInterface
type RealType(v: int) =
member this.Value = v
interface MyInterface
type MyType<'T when 'T :> MyInterface>(element: 'T) =
new() = MyType(EmptyType()) 
member this.X = 0
[<EntryPoint>]
let main _ =
//let a = MyType() //empty constructor, 'T is EmptyType
let b = MyType(RealType(0)) //doesnt work because compiler says 'T is always EmptyType? what?
0

构造函数不能是泛型的。您的类型是泛型的,但每个构造函数都必须返回定义它的确切类型。不能让一个构造函数返回MyType<'t>,另一个构造函数则返回MyType<EmptyType>。如果在MyType<'t>上定义了构造函数,则它必须返回MyType<'t>,而不能返回MyType<EmptyType>

当编译器看到其中一个构造函数总是返回MyType<EmptyType>时,它得出结论,类型参数't必须始终等于EmptyType。正是在这个意义上,参数't被约束为EmptyType,正如错误消息所说。

没有办法让构造函数返回与定义它的类型不同的类型。如果必须专门构造MyType<EmptyType>的实例,则可以使用静态方法:

type MyType<'t>(t: 't) =
static member CreateEmpty () = MyType(EmptyType())

然而,请注意,要调用这样的方法,您仍然需要为MyType提供一些类型参数,例如:

let x = MyType<RealType>.CreateEmpty()

或者你可以使用通配符:

let x = MyType<_>.CreateEmpty()

由于没有根据推断通配符应该是什么,编译器将回退到obj(或最接近的约束(,因此上述调用将等效于MyType<obj>.CreateEmpty()。还是很尴尬。

为了避免这种情况,一个更好的方法是使用一个同名的模块:

module MyType =
let createEmpty() = MyType(EmptyType)
let x = MyType.createEmpty()

此模式广泛用于泛型类型。例如,请参见OptionList

不确定在这种情况下,对重载构造函数的具体限制是什么,导致T受到约束,但使用static member的以下代码可以按需工作:

type MyInterface = interface end
type EmptyType() = interface MyInterface
type RealType(v: int) =
member this.Value = v
interface MyInterface
type MyType<'T when 'T :> MyInterface>(element: 'T) =
static member Empty() = MyType(EmptyType()) 
member this.X = 0
[<EntryPoint>]
let main _ =
let a = MyType<_>.Empty() 
let b = MyType(RealType(0)) 
0

最新更新