Как использовать один и тот же селектор записей двумя способами внутри функции?Линзы? - PullRequest
0 голосов
/ 25 сентября 2018

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

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

1 Ответ

0 голосов
/ 25 сентября 2018

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

{-# 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
...