У меня есть некоторые данные, которые имеют разные представления в зависимости от параметра типа, а-ля Sandy Maguire's Higher Kinded Data< /а>. Вот два примера:
wholeMyData :: MyData Z
wholeMyData = MyData 1 'w'
deltaMyData :: MyData Delta
deltaMyData = MyData Nothing (Just $ Left 'b')
Ниже я привожу некоторые детали реализации, но сначала сам вопрос.
Я часто хочу получить поле данных, обычно через локальное определение, например:
let x = either (Just . Left . myDataChar) myDataChar -- myDataChar a record of MyData
Бывает так часто, что хотелось бы сделать стандартный комбинатор,
getSubDelta :: ( _ -> _ ) -> Either a b -> Maybe (Either c d)
getSubDelta f = either (Just . Left . f) f
но заполнить эту подпись проблематично. Простое решение состоит в том, чтобы просто указать функцию выбора записи дважды,
getSubDelta :: (a->c) -> (b->d) -> Either a b -> Maybe (Either c d)
getSubDelta f g = either (Just . Left . f) g
но это неприлично. Итак, мой вопрос. Есть ли способ, которым я могу заполнить подпись выше? Я предполагаю, что, вероятно, существует решение на основе линз, как оно будет выглядеть? Поможет ли это с глубоко вложенными данными? Я не могу полагаться на то, что типы данных всегда являются одним конструктором, так что призмы? Обходы? Моя игра с объективом слаба, поэтому я надеялся получить совет, прежде чем продолжить.
Спасибо!
Какой-то фон. Я определил общий метод выполнения «дельт» с помощью сочетания GHC.Generics и семейств типов. Суть в том, чтобы использовать семейство типов в определении типа данных. Затем, в зависимости от того, как параметризован тип, записи будут представлять либо целые данные, либо изменение существующих данных.
Например, я определяю бизнес-данные с помощью DeltaPoints
.
MyData f = MyData { myDataInt :: DeltaPoint f Int
, myDataChar :: DeltaPoint f Char} deriving Generic
DeltaPoints
реализованы в библиотеке и имеют разные формы для состояний Delta
и Z
.
data DeltaState = Z | Delta deriving (Show,Eq,Read)
type family DeltaPoint (st :: DeltaState) a where
DeltaPoint Z a = a
DeltaPoint Delta a = Maybe (Either a (DeltaOf a))
Таким образом, DeltaPoint Z a
— это просто исходные данные, a
, а DeltaPoint Delta a
могут присутствовать или отсутствовать, и если они присутствуют, это будет либо замена оригинала (Left
), либо обновление (DeltaOf a
).
Дельта-функции среды выполнения инкапсулированы в класс типов.
class HasDelta a where
type DeltaOf a
delta :: a -> a -> Maybe (Either a (DeltaOf a))
applyDeltaOf :: a -> DeltaOf a -> Maybe a
А с помощью Generics я обычно могу получить дельта-возможности с помощью чего-то вроде:
instance HasDelta (MyData Z) where
type (DeltaOf (MyData Z)) = MyData Delta