Почему логический вывод типа F # отличается от конструкторов? - PullRequest
3 голосов
/ 24 сентября 2011

Этот случай (упрощенный до такой степени, что он не имеет особого смысла) правильно обрабатывается системой типов 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, поэтому его можно вызывать с двумя разными аргументами.

Теперь следующая версия имеет небольшое отличие: вместо передачи существа в качестве аргумента 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).

(Примечание: в этом упрощенном случае Фабрика явно имеет недостатки, потому что она всегда возвращает одно и то же животное / человек, но это поведение не изменится, если вы замените anaimal / aHuman функциями.)

Почему CreatureControlled не создается как универсальный во втором случае? Это ограничение компилятора? Я скучаю по чему-то очень простому (все еще учусь ...)?

Ответы [ 2 ]

3 голосов
/ 24 сентября 2011

В случае конструктора вполне вероятно (или, по крайней мере, неоднозначно), что вы подразумевали, что сам тип является универсальным, например

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

и в F # общие аргументы в определениях типов всегда должны указываться явно.

3 голосов
/ 24 сентября 2011

В первом случае, как вы сказали, Register подразумевает, что он является общим, поэтому он работает.Во втором случае вы передаете два разных типа конструктору неуниверсального класса.Конкретный тип должен быть выведен в этом случае.Если вы добавляете аргументы типа в контроллер существа, он работает:

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

Разница в том, что параметры типа должны быть явными в типах, но не в функциях.Кроме того, конструкторы могут иметь дело только с параметрами типа, объявленными самим типом.

...