и различные сущности.
отаю над кодом, который взаимодействует со схемой базы данных, которая моделирует постоянный граф. Прежде чем перейти к деталям моего конкретного вопроса, я подумал, что это может помочь обеспечить некоторую мотивацию. Моя схема вокруг книг, людей и ролей авторов. Книга имеет много авторских ролей, где в каждой роли есть человек. Однако вместо того, чтобы разрешать прямые запросы UPDATE для объектов книги, вы должны создать новую книгу и внести изменения в новую версию.
Теперь вернемся к земле Хаскелла. В настоящее время я работаю с несколькими типами классов, но главное, что у меня естьHasRoles
а такжеEntity
:
class HasRoles a where
-- Get all roles for a specific 'a'
getRoles :: a -> IO [Role]
class Entity a where
-- Update an entity with a new entity. Return the new entity.
update :: a -> a -> IO a
Вот и моя проблема. Когда вы обновляете книгу, вам нужно создать новую версию книги, но вытакже нужно скопировать поверх предыдущих книг роли (иначе вы потеряете данные). Самый простой способ сделать это:
instance Entity Book where
update orig newV = insertVersion V >>= copyBookRoles orig
Это хорошо, но есть кое-что, что меня беспокоит, и это отсутствие какой-либо гарантии инварианта, что если что-тоEntity
а также HasRoles
, затем вставка новой версии скопирует существующие роли. Я подумал о 2 вариантах:
Одним из «решений» является введениеRequiresMoreWork a b
, Исходя из вышесказанного,insertVersion
теперь возвращаетHasRoles w => RequiresMoreWork w Book
. update
хочетBook
чтобы выйти изRequiresMoreWork
значение, мы могли бы назватьworkComplete :: RequiresMoreWork () Book -> Book
.
Однако реальная проблема заключается в том, что наиболее важной частью головоломки является сигнатура типаinsertVersion
, Если это не соответствует инвариантам (например, он не упомянул о необходимостиHasRoles
) затем все снова разваливается, и мы снова находимся в нарушении инварианта.
Удаляет проблему из времени компиляции, но по крайней мере мы все еще утверждаем инвариант. В этом случае инвариант является чем-то вроде: для всех сущностей, которые также являются экземплярамиHasRoles
вставка новой версии существующего значения должна иметь те же роли.
Я немного озадачен этим. В Лиспе я бы использовал модификаторы методов, в Perl я бы использовал роли, но есть ли что-нибудь, что я могу использовать в Haskell?