2014-11-03 42 views
18

鉴于类型使用透镜读取通过模式匹配多个字段

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) 

有没有一种方法可以在一次遍历中同时提取所有三个的数据结构?

某种方式结合Getter s,Getter s a -> Getter s b -> Getter s (a,b)?在Control.Lens.Reified提供很多关于getter和褶皱非常有用的实例

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

一般来说,newtypes:

+0

你的意思是“序列号”还是这个笑话,我没有得到? – 2014-11-03 20:34:30

+2

汤姆埃利斯:这只是一个糟糕的双关语。 – rampion 2014-11-03 20:41:56

+0

我最初的灵感是[使用模式匹配,这个拉请求试图使用ghc 7.8。*]工作(https://github.com/schell/hdevtools/pull/1)。如果代码使用了现场提取器或镜头,则不需要修复。 – rampion 2014-11-03 20:51:23

回答

22

我们可以使用ReifiedGetter NEWTYPE的Applicative例如,从Control.Lens.Reified

注意#1:请注意,我们将镜头组合为获取者并获取返回的获取者。您无法以这种方式获得复合镜片,因为如果它们的“焦点”重叠,会出现问题。这种情况下适当的二传手行为是什么?

注#2alongside功能可以让您组合两个镜头,得到一个真正的镜头,可以在产品的两个半部分上工作。这与之前的情况不同,因为我们可以确定镜头不重叠。 alongside在你的类型是一个元组或者与元组有同构性时派上用场。

+1

谢谢!我希望你不介意,我在问题中充实了你的回答。 – rampion 2014-11-04 16:11:14

4

充实danidiaz's answer above,我能够用ReifiedGetter构建Getter Prisoner (String, Int, Int)

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)使用alongside,虽然我不得不手动构建PrisonerHList [String, Int, Int]HList [a,b,c](a,b,c)之间Iso'秒。

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) 

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