Я наткнулся на этот вопрос о моделировании наследования в Haskell, и он напомнил мне, что у меня есть немного более сложная версия той же проблемы. Я приведу пример оттуда, потому что это легче, чем придумать мой собственный.
Предположим, что ваша программа содержит несколько типов:
data Camera = Camera ...
data Light = SpotLight ... | DirectionalLight ...
data Object = Monster ... | Player ... | NPC ...
Теперь вы хотите реализовать некоторую базовую физику, поэтому вы хотите, чтобы все они имели положение и скорость, скажем, какого-то типа Vec3
.
Один из способов сделать это - объявить класс Physical
с функциями pos
и vel
и сделать все экземпляры своего типа. Но это означает, что вам нужно изменить все типы, чтобы содержать два Vec3
s, что раздражает, если у вас уже есть много хороших типов, и вы просто хотите приклеить немного функциональности сверху. Решение на основе объектива, предложенное Крисом Тейлором, имеет ту же проблему.
Решение, которое мне кажется неудобным, заключается в объявлении нового конструктора типов,
data Physical a = Physical a Vec3 Vec3
Тогда вам нужно только реализовать экземпляр pos
, vel
и Functor
, и вы сможете сохранить все существующие объявления типов.
Однако... это не очень хорошо. Если теперь вы хотите иметь возможность рисовать свои объекты синим или зеленым или фиолетовым, вы можете сделать то же самое с цветами:
data Coloured a = Coloured a Colour
Но теперь, если у вас есть Coloured Physical Camera
, вы должны fmap
разное количество раз в зависимости от того, хотите ли вы посмотреть его цвет или его положение или его фокусное расстояние. И Coloured Physical Camera
должен быть тем же самым, что и Physical Coloured Camera
, но это не так. Так что это не изящное решение.
Есть ли хороший способ смешивания в разных наборах функций для типов в Haskell? Простое решение, которое работает в простом старом Haskell без языковых расширений или большого количества шаблонов, было бы идеальным, но я также открыт для изучения любой из библиотек, связанных с объективом, если это действительно лучший способ подойти к проблеме.
(Этот старый вопрос о повторном использовании кода в стиле mixins кажется связанным, но я боюсь, что не полностью понимаю вопрос или принятое решение.)