21

给定类型

data Prisoner = P { _name   :: String
                  , _rank   :: Int
                  , _cereal :: Cereal }

data Cereal = C { _number             :: Int
                , _percentDailyValue  :: Map String Float
                , _mascot             :: String }

我可以通过模式匹配提取某人的姓名、等级和谷物编号:

getNameRankAndCerealNumber_0 :: Prisoner -> (String, Int, Int)
getNameRankAndCerealNumber_0 (P { _name=name
                                , _rank=rank
                                , _cereal = C { _number=cerealNumber }}
                             ) = (name, rank, cerealNumber)

或者,我可以使用镜头分别提取每个部分

makeLenses ''Cereal
makeLenses ''Prisoner

getNameRankAndCerealNumber_1 :: Prisoner -> (String, Int, Int)
getNameRankAndCerealNumber_1 p = (p ^. name, p ^. rank, p ^. cereal.number)

有没有办法在一次遍历数据结构中同时提取所有三个?

某种组合Getters, Getter s a -> Getter s b -> Getter s (a,b)?

4

2 回答 2

25

我们可以使用来自以下的newtype的Applicative实例:ReifiedGetterControl.Lens.Reified

runGetter $ (,) <$> Getter number <*> Getter mascot

一般来说,新类型Control.Lens.Reified为 getter 和 folds 提供了很多非常有用的实例。

注意#1:请注意,我们将镜头组合为吸气剂,并获得吸气剂作为回报。您无法以这种方式获得复合镜头,因为如果它们的“焦点”重叠会出现问题。在这种情况下,正确的二传手行为可能是什么?

注意#2旁边功能可让您组合两个镜头,获得适用于产品两半的真正镜头。这与前一种情况不同,因为我们可以确定镜头不会重叠。alongside当您的类型是元组或与元组具有同构时,它会派上用场。

于 2014-11-03T20:56:53.777 回答
7

充实了上面 danidiaz的答案,我能够构建一个Getter Prisoner (String, Int, Int)using ReifiedGetter

getNameRankAndCerealNumber_2 :: Prisoner -> (String, Int, Int)
getNameRankAndCerealNumber_2 = p ^. nameRankAndCerealNumber_2

nameRankAndCerealNumber_2 :: Getter Prisoner (String, Int, Int)
nameRankAndCerealNumber_2 = runGetter ((,,) <$> Getter name <*> Getter rank <*> Getter (cereal.number))

还有一个Lens' Prisoner (String, Int, Int)using alongside,尽管我必须手动构造Iso's between Prisonerand an HList [String, Int, Int]and between HList [a,b,c]and (a,b,c)

getNameRankAndCerealNumber_3 :: Prisoner -> (String, Int, Int)
getNameRankAndCerealNumber_3 p = p ^. nameRankAndCerealNumber_3

setNameRankAndCerealNumber_3 :: (String, Int, Int) -> Prisoner -> Prisoner
setNameRankAndCerealNumber_3 t p = p & nameRankAndCerealNumber_3 .~ t

nameRankAndCerealNumber_3 :: Lens' Prisoner (String, Int, Int)
nameRankAndCerealNumber_3 = hlist . alongside id (alongside id number) . triple
  where triple :: Iso' (a,(b,c)) (a,b,c)
        triple = dimap (\(a,(b,c)) -> (a,b,c)) (fmap $ \(a,b,c) -> (a,(b,c)))
        hlist :: Iso' Prisoner (String, (Int, Cereal))
        hlist = dimap (\(P n r c) -> (n,(r,c))) (fmap $ \(n,(r,c)) -> P n r c)

可能有更简单的方法可以做到这一点,但这是另一个问题。

于 2014-11-07T03:17:32.640 回答