我有一些数据,这些数据基于类型参数具有不同的表示形式,例如桑迪·马奎尔(Sandy Maguire(的高级种类数据。下面是两个示例:
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执行"增量"的通用方法。泛型和类型族。要点是在数据类型的定义中使用类型族。然后,根据类型的参数化方式,记录将表示整个数据或对现有数据的更改。
例如,我使用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
通过使用泛型,我通常可以通过以下方式获得增量功能:
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