为什么F#泛型类型推断在构造函数上有所不同?

时间:2011-09-23 20:06:31

标签: types f#

这种情况(简化到没有多大意义)由F#的类型系统正确处理:

type HumanBeing() = class end
type Animal() = class end

type CreatureController() =
    member this.Register creature = creature

type CreatureFactory() =
    let anAnimal = new Animal()
    let aHuman = new HumanBeing()

    member this.GiveMeAnAnimal  =
        (new CreatureController()).Register anAnimal

    member this.GiveMeAHuman =
        (new CreatureController()).Register aHuman

正确推断出CreatureController.Register的类型:'a - > 'a,因此它可以用两个不同的参数调用。

现在以下版本略有不同:不是将creature作为参数传递给CreatureController.Register,而是传递给它的构造函数。

type HumanBeing() = class end
type Animal() = class end

type CreatureController(creature) =
    member this.Register = creature

type CreatureFactory() =
    let anAnimal = new Animal()
    let aHuman = new HumanBeing()

    member this.GiveMeAnAnimal =
        (new CreatureController(anAnimal)).Register

    member this.GiveMeAHuman =
        (new CreatureController(aHuman)).Register

第二个示例无法编译,因为Register被推断为Animal,因此您无法调用new CreatureController(aHuman)

(注意:在这个简化的情况下,Factory显然有缺陷,因为它总是返回相同的animal / humanBeing,但是如果用函数替换anAnimal / aHuman,这种行为不会改变。)

为什么在第二种情况下,CreatureControlled不是通用的?这是编译器限制吗?我错过了一些非常基本的东西(还在学习......)?

2 个答案:

答案 0 :(得分:3)

在第一种情况下,如您所述,Register被推断为通用的,因此它有效。在第二种情况下,您将两种不同的类型传递给非泛型类的构造函数。在这种情况下必须推断出具体类型。如果你将类型args添加到生物控制器,它可以工作:

type HumanBeing() = class end
type Animal() = class end

type CreatureController<'T>(creature:'T) =
    member this.Register = creature

type CreatureFactory() =
    let anAnimal = new Animal()
    let aHuman = new HumanBeing()

    member this.GiveMeAnAnimal =
        (new CreatureController<_>(anAnimal)).Register

    member this.GiveMeAHuman =
        (new CreatureController<_>(aHuman)).Register

区别在于类型参数必须在类型上是显式的,而不是在函数上。此外,构造函数只能处理类型本身声明的类型参数。

答案 1 :(得分:3)

在构造函数的情况下,你可能(或至少是模棱两可的)你认为类型本身是通用的,例如。

type CreatureController<'T>(creature:'T) = ...

并且在F#中,必须始终明确指定类型定义的泛型参数。