Как я могу продвигать повторное использование кода таким же образом, как миксины / модификаторы методов / трейты на других языках?

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

А теперь вернемся в страну Haskell. В настоящее время я работаю с несколькими классами типов, но, что важно, у меня есть 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 . обновлению требуется Книга , поэтому, чтобы выйти из значения RequiresMoreWork , мы могли бы вызвать workComplete :: RequiresMoreWork () Book -> Book .

Однако настоящая проблема заключается в том, что наиболее важной частью головоломки является сигнатура типа insertVersion . Если это не соответствует инвариантам (например, не упоминается о необходимости HasRoles ), тогда все снова разваливается, и мы возвращаемся к нарушению инварианта.

Докажите это с помощью QuickCheck.

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


Я немного озадачен это. В Lisp я бы использовал модификаторы методов, в Perl я бы использовал роли, но есть ли что-нибудь, что я могу использовать в Haskell?

9
задан ocharles 29 August 2011 в 20:33
поделиться