Using a Lens to read multiple fields

前端 未结 2 1872
难免孤独
难免孤独 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.

    0 讨论(0)
  • 2020-12-15 06:03

    We can use the Applicative instance of the ReifiedGetter newtype from Control.Lens.Reified:

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

    In general, the newtypes in Control.Lens.Reified offer a lot of very useful instances for getters and folds.

    Note#1: Notice that we are combining the lenses as getters, and getting a getter in return. You can't obtain a composite lens in this way, as there would be problems if their "focuses" overlap. What could be the proper setter behaviour in that case?

    Note#2: The alongside function lets you combine two lenses, getting a bona-fide lens that works on the two halves of a product. This is different form the previous case because we can be sure the lenses don't overlap. alongside comes in handy when your type is a tuple or has an isomorphism to a tuple.

    0 讨论(0)
提交回复
热议问题