У меня есть некоторые данные, которые имеют разные представления, основанные на параметре типа, такие как Данные высшего класса Сэнди Магуайра .Вот два примера:
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