This case (simplified to the point that it doesn’t make much sense) is handled correctly by the F#’s type system:
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
The type of CreatureController.Register is correctly inferred: ‘a -> ‘a, thus it can be called with the two different arguments.
Now the following version has a slight difference: instead of passing creature as argument to CreatureController.Register, it is passed to its constructor.
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
This second example does not compile because Register is inferred as Animal, so you cannot call new CreatureController(aHuman).
(Note: in this simplified case the Factory is obviously flawed because it always return the same animal/humanBeing, but this behavior does not change if you replace anAnimal/aHuman with functions.)
Why isn’t CreatureControlled created as generic in the second case? Is this a compiler limitation? Am I missing something very basic (still learning…)?
In the first case, as you stated,
Registeris inferred to be generic, so it works. In the second case, you’re passing two different types to the constructor of a non-generic class. A concrete type must be inferred in this case. If you add type args to creature controller, it works:The difference is type parameters must be explicit on types, but not on functions. Also, constructors may only deal with type parameters declared by the type itself.