2014-11-23 57 views
4

考虑以下示例代码,其中我有一个泛型类型和2个静态成员构造函数,用于创建上述类型的专用实例。为什么F#在这种情况下不能推断出类型?

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" 

我想我有必要的类型注释到位,但F#给了我警告。 E.g:

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, e.g. 'Cell<_>'.

我怎样才能使警告消失?

+4

提示:您正在使用_generic_类型的静态成员。 – ildjarn 2014-11-23 20:21:02

+1

@ildjarn你应该发布作为答案:-) – 2014-11-23 20:31:25

回答

4

正如@ildjarn暗示的那样,Cell是一个泛型类型,编译器在调用静态成员时想知道类型'T

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

一种避免指定'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" 

然而,因为你反正指定的函数名所需的类型,下面的语法可以是优选的。

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 

感谢@Vandroiy在我Create方法指出缺失的类型约束,他的回答,显示了编译器可以推断'T为通用型Cell时,它可以通过静态方法来决定如何调用。

+0

谢谢!请注意,模块解决方案对我来说不起作用,因为我的真实示例依赖于可选参数 – rgrinberg 2014-11-24 03:34:41

+0

@rgrinberg和cadull:我不认为这是好的设计。如果你用'Cell .Create 1.0'代替最后一行,它就和“有效”一样。 (查看我的回答) – Vandroiy 2014-11-24 12:54:58

+1

为什么人们会不断努力?第二种解决方案,OP显然与之相关,存在严重的问题!问题不仅限于通用类型。 'IntCell.Create“hello”'也编译没有问题。 – Vandroiy 2014-11-24 13:33:34

4

编译器无法确定方法CreateIntCreateFloat的通用参数'T,因为它与方法的返回类型无关。在的问题,它是有效的写:

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

但是,您可以一样好写

Cell<string>.Create 1.0 // Trollolol 

为了避免这种情况,你需要确保工厂只能生产它的类型拜访。在泛型类型上声明工厂时,使用类型注释将其返回类型的泛型参数等同于所调用类型的泛型参数。

在我看来,复杂的表述增加了混淆。你可以达到预期的效果与

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

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

注为x类型标注,与Cell<>类型的泛型参数相当于工厂的输入型!

编辑以报告的评价:

原样,类型IntCellStringCell没有意义;它们只是Cell<int>Cell<string>的可读性较差的形式。从评论到这个答案,我明白这些类型应该暴露,而不是Cell。据我所知,如果在问题中定义它们,这是不可能的,因为类型缩写最多具有它们缩写类型的可访问性。

这是一个合理的设计选择:如果一个类型是泛型的,它应该接受所有有效的泛型类型参数。如果IntCellStringCell添加专门的实现,通常的方法是组合它们的Cell类型及其专用功能的适当实例。然后,Cell类型被允许具有比专门类型更受限制的可访问性。

+0

单元类型只是一个例子。在我最初的设计中,泛型是没有意义的,不应该被构造。只有2个专业被暴露 – rgrinberg 2014-11-24 16:34:50

+0

@rgrinberg我不认为这会工作;专业化是类型缩写,因此将受到Cell <>类型的可访问性的限制。见编辑的答案。 – Vandroiy 2014-11-24 17:15:01

+0

很酷的答案,我不知道你可以省略这种类型的通用规范;我习惯看到像'Cell <_>'这样的代码。由于它声明了创建函数的返回类型,原始问题在我的回答中没有表现出问题。我会纠正我的答案。 – cadull 2014-11-25 03:19:46

相关问题