如何在函数中以两种方式使用相同的记录选择器?镜片?

How to use the same record selector two ways within a function? Lenses?

我有一些数据根据类型参数具有不同的表示形式,la 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 和类型族定义了执行 "deltas" 的通用方法。要点是在数据类型的定义中使用类型族。然后,根据类型的参数化方式,记录将代表整个数据或对现有数据的更改。

例如,我使用DeltaPoints定义业务数据。

MyData f = MyData { myDataInt  :: DeltaPoint f Int
                  , myDataChar :: DeltaPoint f Char} deriving Generic

DeltaPoints 在库中实现,DeltaZ 状态有不同的形式。

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 只是原始数据,aDeltaPoint Delta a 可能存在也可能不存在,如果存在则将替换为原始 (Left) 或更新 (DeltaOf a).

运行时增量功能封装在类型 class.

class HasDelta a where
  type DeltaOf a
  delta :: a -> a -> Maybe (Either a (DeltaOf a))
  applyDeltaOf :: a -> DeltaOf a -> Maybe a

通过使用泛型,我通常可以通过以下方式获得增量功能:

instance HasDelta (MyData Z) where
  type (DeltaOf (MyData Z)) = MyData Delta

我想你可能想要:

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