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

У меня есть некоторые данные, которые имеют разные представления в зависимости от параметра типа, а-ля 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

person trevor cook    schedule 25.09.2018    source источник


Ответы (1)


Я думаю, вы, вероятно, хотите:

{-# LANGUAGE RankNTypes #-}
getSubDelta :: (forall f . (dat f -> DeltaPoint f fld))
            -> Either (dat Z) (dat Delta)
            -> Maybe (Either (DeltaPoint Z fld) (DeltaOf fld))
getSubDelta sel = either (Just . Left . sel) sel

давая:

x :: Either (MyData Z) (MyData Delta) 
     -> Maybe (Either (DeltaPoint Z Char) (DeltaOf Char))
x = getSubDelta myDataChar
-- same as: x = either (Just . Left . myDataChar) myDataChar
person K. A. Buhr    schedule 25.09.2018