2014-10-04 101 views
0

假設我有如下定義的數據類型:如何兩個簡單的鏡頭,結果合併成一個

data Register = Register { _reg_h :: Word8 
         , _reg_l :: Word8 
         } 
makeLenses ''Register 

現在,如果我想定義焦點從RegisterWord16一個鏡頭。這個功能應該是一些象下面這樣:

refer :: Lens' Register Word16 
refer = do h <- reg_h 
      l <- reg_l 
      return $ (fromEnum h `shiftL` 8) .&. fromEnum l 

Word8Word16shiftL,並.&.Data.WordData.Bits,我已經在我的源代碼開始啓用RankNTypes

然而,這代碼根本不起作用。我想這可能是因爲完整的Lens類型定義有四個類型參數,並且這會比簡單的Lens'更簡單,以充當簡單的Monad

那麼以什麼方式可以達到上述效果呢?

謝謝。

回答

3

你不能用這樣的符號定義鏡頭。鏡頭需要包含關於如何從註冊表中獲取Word16的概念,以及如何設置Word16返回客棧。您的代碼只說明如何從註冊表中獲取Word16。因此,它更應被寫成直功能:

registerToWord16 :: Register -> Word16 
registerToWord16 r = (fromEnum (view reg_h r) `shiftL` 8) .&. fromEnum (view reg_l r) 

如果你只是想從一個寄存器WORD16,您可以使用該功能 toControl.Lens.Getter,得到一個getter,你可以撰寫鏡頭。

如果您希望能夠以其他方式進行操作,以便您可以從Word16中設置註冊表,則可能需要編寫一個Iso。爲了將ISO寫入您需要定義走另一條路的功能:

word16ToRegister :: Word16 -> Register 
word16ToRegister = ... 

現在,您可以使用iso :: (a -> b) -> (b -> a) -> Iso' a b函數來創建你的參考。

refer :: Iso' Register Word16 
refer = iso registerToWord16 word16ToRegister 

一個ISO是在lens hierarchy比鏡頭更高,所以如果你用鏡頭譜寫它,你將得到一個鏡頭。

編輯:

所以你要一個寄存器包含多個字段中的評論澄清。在這種情況下,你需要一個鏡頭。您可以手動編寫它像安德拉斯表現在他的崗位,或者你可以使用lens :: (s -> a) -> (s -> a -> s) -> Lens' s a功能從getter和setter這樣的構造是:使用lens庫,你應該看看Reite的

refer :: Lens' Register Word16 
refer = lens get set 
    where 
     get :: Register -> Word16 
     get = registerToWord16 

     set :: Register -> Word16 -> Register 
     set reg w16 = reg & reg_h .~ word16ToRegH w16 
          & reg_l .~ word16ToRegL w16 

     word16ToRegH :: Word16 -> Word8 

     word16ToRegL :: Word16 -> Word8 
+0

寄存器類型可能包含設置地址時要保留的其他字段。在這種情況下,iso不適用於此。我可以用類似的方式定義一個鏡頭嗎? – 2014-10-05 10:30:36

3

對於一個地道的解決方案回答。在這裏,我給出了更多的直接答案,並展示如何實現你想要的鏡頭。

Lens類型同義詞的定義是Lens s t a b = Functor f => (a -> f b) -> s -> f t。通過實現這種類型的功能(特定於某些s t a b)可以實現定製鏡頭。

一般情況下,通過查找s類型的上下文中a類型的一些價值,應用a -> f b功能給它,然後使用fmap包裹上下文的其餘部分所產生的f b圍繞實現一個鏡頭。一個簡單的例子:

-- we'd like to focus on the first element of a pair. 
-- we change the type of the first elem from a to a'. 
-- thus the type of the whole pair changes from (a, b) to (a', b) 

_1 :: Lens (a, b) (a', b) a a' 
_1 f (a, b) = fmap (\a' -> (a', b)) (f a) 
        |    | 
        |    ---> apply f to the focus 
        | 
        --> wrap the result back 

我們可以實現鏡頭Register類似:

refer :: Lens' Register Word16 
refer f (Register h l) = fmap wrap (f val) where 
    val = fromIntegral (shiftL h 8) .&. fromIntegral l 
    wrap n = Register (fromIntegral $ shiftR n 8) (fromIntegral $ 0xffff .&. n) 

我們再次申請f我們想要關注的價值,然後再包將結果返回。由於需要合併和拆分數字,定義只會稍微複雜。

相關問題