Тип строительных лесов / коммутативный состав - PullRequest
2 голосов
/ 13 июля 2020

У меня есть 3 типа: Task (единица работы), Estimate (период времени) и User.

В моем приложении я бы хотел «украсить» мои Task s, чтобы получить EstimedTask (можно суммировать), AssignedTask (для сортировки) и AssignedEstimatedTask (которые являются обоими и к которым я могу применить алгоритм Ганта)

Моя первая попытка - реализовать их с помощью кортежей:

EstimatedTask :: (Task, Estimate)
AssignedTask :: (Task, User)
AssignedEstimatedTask :: (Task, Estimate, User)

, но затем, если я хочу построить AET, мне нужно определить 3 конструктора

T -> E -> U -> AET
(T, E) -> U -> AET
(T, U) -> E -> AET

Поскольку я собираюсь украсить даже больше, боюсь, количество конструкторов взорвется.

Есть ли умный способ сделать это? В частности, существует ли предпочтительный способ сделать субконструктор коммутативным (например, assign . estimate $ task == estimate . assign $ task?

1 Ответ

1 голос
/ 14 июля 2020

Определите data Controlled :: Type -> Bool -> Type так, чтобы Controlled t False был одноэлементным типом (например, (), без информации), а Controlled t True был копией t:

data Controlled :: Type -> Bool -> Type where
    Exists :: a -> Controlled a True
    NonExist :: Controlled a False

Затем определите " полный "тип, представляющий все, что у вас есть, с Controlled слотами:

data AssignedEstimatedTask' :: Bool -> Bool -> Type where
    AssignedEstimatedTask ::
        { getTask :: Task
        , taskUser :: Controlled User a
        , taskEstimate :: Controlled Estimate b } ->
        AssignedEstimatedTask' a b

Введите кучу синонимов:

-- naming the types is exponential!
type JustTask = AssignedEstimatedTask' False False
type AssignedTask = AssignedEstimatedTask' True False
type EstimatedTask = AssignedEstimatedTask' False True
type AssignedEstimatedTask = AssignedEstimatedTask' True True

justTask :: Task -> JustTask
justTask t = AssignedEstimatedTask t NonExist NonExist

И теперь вы можете добавлять элементы в задачу, с использованием обновлений записей, которые отслеживаются во время компиляции:

jTask :: JustTask
aTask :: AssignedTask
eTask :: EstimatedTask
aeTask, eaTask :: AssignedEstimatedTask
-- ofc, all of ^ could have been inferred
jTask = justTask someTask
aTask = jTask { taskUser = Exists someUser }
eTask = jTask { taskEstimate = Exists someEstimate }
aeTask = aTask { taskEstimate = Exists someEstimate }
eaTask = eTask { taskUser = Exists someUser }
-- aeTask == eaTask

Рабочий пример

Если вы действительно хотите извлечь обновления записей как функции, они выглядят так это

assignTask :: User -> AssignedEstimatedTask' a e -> AssignedEstimatedTask' True e
assignTask u t = t { taskUser = Exists u }
estimateTask :: Estimate -> AssignedEstimatedTask' a e -> AssignedEstimatedTask' a True
estimateTask e t = t { taskEstimate = Exists e }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...