简体   繁体   English

为什么F#不能在这种情况下推断出类型?

[英]Why can't F# infer the type in this case?

Consider the following sample code where I have a generic type and 2 static member constructors that create a specialized instance of the said type. 考虑以下示例代码,其中我有一个泛型类型和2个静态成员构造函数,它们创建所述类型的专用实例。

type Cell<'T> = { slot: 'T }
with
    static member CreateInt x : IntCell  = { slot = x }
    static member CreateString x : StringCell = { slot = x}
and IntCell = Cell<int>
and StringCell = Cell<string>

// Warnings on the next 2 lines
let x = Cell.CreateInt 123
let y = Cell.CreateString "testing"

I think I have the necessary type annotations in place and yet F# gives me warnings. 我认为我有必要的类型注释,但F#给了我警告。 Eg: 例如:

Warning 2 The instantiation of the generic type 'Cell' is missing and can't be inferred from the arguments or return type of this member. Consider providing a type instantiation when accessing this type, eg 'Cell<_>'.

How can I make the warning go away? 如何让警告消失?

As hinted by @ildjarn, Cell is a generic type and the compiler wants to know the type 'T when calling the static member. 正如@ildjarn暗示的那样, Cell是一个泛型类型,编译器想要在调用静态成员时知道类型'T

// Two ways to fix the compiler warning
let x = Cell<int>.CreateInt 123
let y = StringCell.CreateString "testing"

A way to avoid specifying 'T is to move the create functions into a module. 避免指定'T是将创建函数移动到模块中。

type Cell<'T> = { slot: 'T }
type IntCell = Cell<int>
type StringCell = Cell<string>
module Cell =
    let createInt x : IntCell = { slot = x }
    let createString x : StringCell = { slot = x }

let x = Cell.createInt 123
let y = Cell.createString "testing"

However, since you specify the desired type in the function name anyway, the following syntax may be preferred. 但是,由于您无论如何都要在函数名中指定所需的类型,因此可能首选以下语法。

type Cell<'T> = { slot: 'T }
with
    static member Create (x : 'T) = { slot = x }
type IntCell = Cell<int>
type StringCell = Cell<string>

let x = IntCell.Create 123
let y = StringCell.Create "testing"

// or simply
let z = Cell<float>.Create 1.0

Thanks to @Vandroiy for pointing out the missing type constraint in my Create method and for his answer that shows how the compiler can infer 'T for the generic type Cell when it can be determined by the static method being called. 感谢@Vandroiy指出我的Create方法中缺少的类型约束,以及他的答案,该答案显示了编译器如何通过调用静态方法确定通用类型Cell 'T值。

The compiler cannot determine the generic parameter 'T of the methods CreateInt and CreateFloat because it is unrelated to the methods' return types. 编译器无法确定方法CreateIntCreateFloat的泛型参数'T ,因为它与方法的返回类型无关。 In the question, it is valid to write: 在这个问题中,写下来是有效的:

Cell<float>.Create 1.0 // useless type annotation to remove warning

However, you can just as well write 但是,你也可以写

Cell<string>.Create 1.0 // Trollolol

To avoid this, you need to make sure the factory can only produce the type it is called on. 为避免这种情况,您需要确保工厂只能生成它所调用的类型。 When declaring a factory on a generic type, use a type annotation to equate the generic argument of its return type with the generic argument of the type it is called on. 在泛型类型上声明工厂时,使用类型注释将其返回类型的泛型参数与其调用类型的泛型参数等同。

In my opinion, the complicated formulation adds to the confusion. 在我看来,复杂的表述增加了混乱。 You can achieve the desired effect with 你可以达到预期的效果

type Cell<'T> =
    { slot: 'T }
    static member Create (x : 'T) = { slot = x }

let x = Cell.Create 123
let y = Cell.Create "testing"

Note the type annotation for x that equates the factory's input type with the generic parameter of the Cell<> type! 请注意x的类型注释,它将工厂的输入类型与Cell<>类型的泛型参数等同起来!

Edited to address the comment: 编辑发表评论:

As is, the types IntCell and StringCell serve no purpose; IntCellStringCellStringCell类型没有用处; they are just a less readable form of Cell<int> and Cell<string> . 它们只是Cell<int>Cell<string>一种不太可读的形式。 From a comment to this answer, I understand that these types should be exposed instead of Cell . 从评论到这个答案,我理解应该暴露这些类型而不是Cell As far as I know, this is not possible if they are defined as in the question, since type abbreviations have at most the accessibility of the type they abbreviate. 据我所知,如果在问题中定义它们是不可能的,因为类型缩写最多具有它们缩写的类型的可访问性。

This is a reasonable design choice: if a type is generic, it should accept all valid generic type arguments. 这是一个合理的设计选择:如果类型是通用的,它应该接受所有有效的泛型类型参数。 If IntCell and StringCell add specialized implementation, the usual way is to compose them of the appropriate instantiation of the Cell type and their specialized features. 如果IntCellStringCell添加了专门的实现,通常的方法是组合它们适当的Cell类型实例化及其专用功能。 Then, the Cell type is allowed to have a more restricted accessibility than the specialized types. 然后,允许Cell类型具有比专用类型更受限制的可访问性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM