Наследование модели с использованием типов данных в стиле функционального программирования - PullRequest
3 голосов
/ 04 сентября 2010

Я недавно использовал F # и пытался кодировать функционально, а не делать ООП заново в другом синтаксисе. Теперь я столкнулся с проблемой, которую мог бы решить с помощью сочетания наследования и различающихся союзов, но для которого я пытаюсь найти чисто функциональное представление стиля.

То, что я хочу смоделировать, выглядит примерно так (изменено для сохранения шаблона, поскольку я не могу использовать реальный код):

type Shape =
    | Rectangle of Size * Size
    | Circle of Diameter

пока все хорошо, но теперь мне нужно представить набор дополнительных свойств, относящихся к различным типам фигур, таких как:

type ShapeProperty =
    | Color of Shape * Color // Fine, valid for all shapes
    | Rotation of Shape * Angle // Wants to be Rotation of Rectangle * Angle
    | Details of Shape * int // Wants to be Detail of Circle * int

Если бы вместо использования различенного объединения для Shape я бы использовал базовый класс и наследование, я мог бы ссылаться на фактические типы и убедиться, что вращение может применяться только к Rectangle, а не к Circle, но теперь я не могу , Есть ли способ реализовать что-то подобное при сохранении чисто функциональных структур данных?

Edit:

Мое текущее решение состоит в том, чтобы отделить определение отдельной фигуры от факта, что фигуры вообще связаны, как это:

type Rectangle = Rectangle of Size * Size // Or using a record type
type Circle = Circle of Diameter // Or using a record type
type Shape = RectangleShape of Rectangle | CircleShape of Circle

, что означает, что у меня есть тип для ссылки в ShapeProperty:

type ShapeProperty =
    | Color of Shape * Color
    | Rotation of Rectangle * Angle
    | Details of Circle * int

Это кажется немного неуклюжим, поскольку теперь нужно инкапсулировать каждую фигуру в типе Shape, чтобы сохранить их в коллекции, но это дает мне способ выразить безопасность типов, к которой я стремлюсь. Любые улучшения будут приветствоваться.

1 Ответ

9 голосов
/ 02 декабря 2010

Я думаю, что дискриминационные союзы могут быть просто неправильным инструментом для частей этой работы.Только то, что вы пишете функциональный код, не означает, что вы должны выбросить все, что вы знаете.

Дискриминационные союзы являются (в некотором смысле, и есть своего рода предостережением) инверсией наследования (производные типы), мы могли бы назвать их составными типами.

В моделях производных типов гарантированно верно все, что касается родителя в отношении потомков (LSP), в этой модели составных типов все в отношении потомков гарантированно вернородитель. (т.е. вместо «новая собака: животное, новая кошка: животное, это новая собака, кошка: кошка», так что вы получите новую конструкцию всех утверждений, которые верны в отношении кошек и собак. Думайте об этом как о пересечениивсе утверждения как о кошке, так и о собаке.)

Таким образом, возникает вопрос: «Как мы работаем с производными типами в OCaml?». Ну, «O» там означает объект ...

Я бы предложил вам создать объектную модель с производными типами для вашей иерархии фигур, так как у вас есть верхний тип (форма) и нижний тype (пустая форма или ноль).И затем вы создаете свои ADT из этих объектов, поэтому у вас есть

abstract Shape, Circle: Shape, Rectangle: Shape,

Тогда у вас есть

type ShapeProperty = |Фу Формы |Бар прямоугольника |Blah of Circle ;;

Все, что знает о свойстве shape, должно быть в состоянии обработать все три из этих случаев (разумно, поскольку это то, для чего оно у вас есть), а окружность и прямоугольник - это присвоение, совместимое с shape,так что вам не нужно беспокоиться о соглашениях по боксу.

Для чего это стоит, YMMV и т. д. Haskell (другой основной диалект ML, бегающий вокруг) не использует объекты для производных типов, он использует то, что известнокак «классы типов», которые представляют собой наборы свойств / поведений, которые вы «извлекаете из» и «гарантируете реализацию», тем самым позволяя функции принимать конкретный экземпляр класса типов.

В то время как гораздо более функциональныйв своей философии попытка выяснить, что происходит, вызывает у меня головную боль, поскольку обычные абстракции, которые я использую для подобных объектам конструкций, больше не применяются, и мне приходится использовать эти конструкции, которые логически эквивалентны, но семантически существенно более необычны (хотя я знаю, чтомного людей, которые используют йони имеют большой успех, так что это могут быть только мои собственные ошибки).

Обратите внимание на предостережение, поскольку тегированные объединения помечены, все, что вам нужно сделать, это гарантировать семантическое действие для всех тегов.Это действие может быть «неудачным», почему вы даете мне один из них, я понятия не имею, что с ним делать ».Проблема, как вы заметили в этой конструкции, заключается в том, что при этом вы теряете много действительно крутых аспектов безопасности типов.Например, вы пишете функцию, которая принимает параметр int, а затем, если для этого параметра ничего не указывается, вы бросаете исключение, ну почему вы сказали, что знаете, как обрабатывать параметр int?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...