2014-11-03 14 views
18

Với loạiSử dụng một ống kính để đọc nhiều lĩnh vực

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

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

tôi có thể trích xuất tên của ai đó, cấp bậc, và số ngũ cốc qua khớp mẫu:

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

Cách khác, tôi có thể sử dụng ống kính để trích xuất từng phần riêng biệt

makeLenses ''Cereal 
makeLenses ''Prisoner 

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

Có cách nào trích xuất cả ba đồng thời trong một lần truyền tải đơn lẻ cấu trúc dữ liệu?

Một số cách để kết hợp Getter s, Getter s a -> Getter s b -> Getter s (a,b)?

+0

Bạn có nghĩa là "số sêri" hay đây là một số trò đùa mà tôi không nhận được? –

+2

Tom Ellis: Nó chỉ là một trò chơi xấu. – rampion

+0

nguồn cảm hứng ban đầu của tôi là [việc sử dụng mẫu phù hợp với yêu cầu kéo này cố gắng để làm việc với ghc 7.8. *] (Https://github.com/schell/hdevtools/pull/1). Nếu mã đã sử dụng máy nhổ hoặc ống kính trường, thì nó sẽ không cần phải sửa chữa. – rampion

Trả lời

22

Chúng tôi có thể sử dụng Applicative dụ của ReifiedGetter Newtype từ Control.Lens.Reified:

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

Nhìn chung, newtypes trong Control.Lens.Reified phục vụ rất nhiều trường hợp rất hữu ích cho getter và nếp gấp.

Lưu ý # 1: Lưu ý rằng chúng tôi đang kết hợp các ống kính dưới dạng getters, và nhận được một getter trong trở lại. Bạn không thể có được một ống kính tổng hợp theo cách này, vì sẽ có vấn đề nếu chồng chéo "tập trung" của chúng. Điều gì có thể là hành vi setter thích hợp trong trường hợp đó?

Lưu ý # 2: Chức năng alongside cho phép bạn kết hợp hai ống kính, nhận được một ống kính song song hoạt động trên hai nửa của sản phẩm. Đây là hình thức khác nhau trường hợp trước bởi vì chúng tôi có thể chắc chắn rằng các ống kính không chồng lên nhau. alongside có ích khi loại của bạn là một tuple hoặc có một đẳng cấu cho một tuple.

+1

Cảm ơn! Tôi hy vọng bạn không nhớ, tôi đã đưa ra câu trả lời của bạn một chút trong câu hỏi. – rampion

4

cho thịt ra danidiaz's answer above, tôi đã có thể xây dựng một Getter Prisoner (String, Int, Int) sử dụng 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)) 

Và một Lens' Prisoner (String, Int, Int) sử dụng alongside, mặc dù tôi đã tự xây dựng Iso' s giữa Prisoner và một HList [String, Int, Int] và giữa HList [a,b,c](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) 

Có thể có cách dễ dàng hơn để thực hiện việc này, nhưng đó là một câu hỏi khác.

Các vấn đề liên quan