2014-11-03 77 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) 

可能有更簡單的方法來做到這一點,但那是另一個問題。