Using a Lens to read multiple fields

前端 未结 2 1880
难免孤独
难免孤独 2020-12-15 05:12

Given the types

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

data Cereal = C { _number           


        
2条回答
  •  死守一世寂寞
    2020-12-15 06:00

    Fleshing out danidiaz's answer above, I was able to construct a 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))
    

    And a Lens' Prisoner (String, Int, Int) using alongside, though I had to manually construct Iso's between Prisoner and 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)
    

    There may be an easier way to do this, but that's another question.

提交回复
热议问题