【问题标题】:Compiler error in F# when creating a secondary, non-generic constructor创建辅助非泛型构造函数时 F# 中的编译器错误
【发布时间】:2020-08-27 23:50:44
【问题描述】:

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

此示例显示了错误(可在 F# Playground 中重现)。请注意,只需注释第二个构造函数(第 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

【问题讨论】:

    标签: generics interface f#


    【解决方案1】:

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

    当编译器看到构造函数之一总是返回MyType&lt;EmptyType&gt;时,它会断定类型参数't必须总是等于EmptyType。正是在这个意义上,参数't 被限制为EmptyType,如错误消息所述。

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

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

    但是,请注意,要调用此类方法,您仍然需要为 MyType 提供一些类型参数,例如:

    let x = MyType<RealType>.CreateEmpty()
    

    或者您可以使用通配符:

    let x = MyType<_>.CreateEmpty()
    

    在没有依据推断通配符应该是什么的情况下,编译器将退回到obj(或最近的约束),因此上述调用将等效于MyType&lt;obj&gt;.CreateEmpty()。还是很尴尬。

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

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

    这种模式广泛用于泛型类型。例如,请参阅 OptionList

    【讨论】:

      【解决方案2】:

      不确定重载构造函数的具体限制是什么导致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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-26
        • 2018-02-25
        • 1970-01-01
        相关资源
        最近更新 更多