我正在嘗試使用 Haskell 中的鏡頭和棱鏡訪問嵌套記錄:
import Data.Text (Text)
import Control.Lens.TH
data State = State
{ _stDone :: Bool
, _stStep :: StateStep
}
data StateStep
= StatePause
| StateRun
{ _stCounter :: Int
, _stMMistake :: Maybe Text
}
makeLenses ''State
makeLenses ''StateStep
makePrisms ''StateStep
main :: IO ()
main = do
let st = State False $ StateRun 0 Nothing
-- works, but the `_2` seems weird
mMistake = st ^? stStep . _StateStepRun . _2 . _Just
-- why not something like (the following does not compile)
mMistake = st ^. stStep . _StateStepRun . _Just . stMMistake
行之有效的路線留下了一些問題。我不確定型別是否巧合。該欄位_stMMistake具有型別Maybe Text,但是呢?
let st = State False StatePause
? 我錯過了明確的join.
我對棱鏡的作業原理一無所知。雖然棱鏡給我一個元組似乎是合乎邏輯的,但同時我期待一些可組合的東西,因為我可以使用鏡頭更深入地了解我的嵌套結構。我是否必須為此手動派生我的實體,也許?
uj5u.com熱心網友回復:
這是您的第一個作業的方式/原因mMistake...
棱鏡是一種聚焦于“部分”的光學器件,該“部分”可能存在也可能不存在于“整體”中。在您的示例中,_StateRun和_Just都是棱鏡。_Just棱鏡聚焦于整體的a一部分Maybe a。這樣的一個a可能存在也可能不存在。如果Maybe avalue 是Just xfor some x :: a,則該部分a 存在并且具有 value x,這就是_Just重點。如果Maybe a值為Nothing,則該部分a不存在,并且不_Just關注任何內容。
你的 prism 有點類似_StateRun。如果整體StateStep是一個StateRun x y值,則_StateRun關注那個“部分”,表示為StateRun建構式欄位的元組,即(x, y) :: (Int, Maybe Text). 另一方面,如果整體StateStep是 a StatePause,則該部分不存在,并且棱鏡不會聚焦于任何東西。
當您組合棱鏡,如_StateRun和_Just,以及鏡頭,如stStep和_2時,您將創建一個新的棱鏡,它結合了組合的一系列聚焦操作。所以考慮棱鏡:
prism1 = stStep . _StateRun . _2 . _Just
這個棱鏡視圖是一個整體的型別State。第一個鏡頭stStep專注于其StateStep領域。如果那StateStep是一個StateRun x (Just y)值,那么_StateRun棱鏡聚焦在(x, Just y)零件上,而_2鏡頭進一步聚焦在Just y零件上,而_Just棱鏡進一步聚焦在y :: Text零件上。
另一方面,如果StateStep場是 a StatePause,棱鏡prism1不聚焦任何東西(因為第二個分量 prism_StateRun不聚焦任何東西),如果它是 a StateRun x Nothing,棱鏡prism1 仍然不聚焦任何東西,因為即使雖然_StateRun可以聚焦,(x, Nothing)也_2可以聚焦Nothing,但最終_Just沒有聚焦任何東西,所以整個棱鏡無法聚焦。
特別是,在處理 a并嘗試參考丟失的第二個欄位或類似的任何內容_2時,鏡頭不會“失火” 。StatePause您曾經_StateRun專注于StateRun建構式的欄位元組這一事實確保了在整個棱鏡聚焦時所需的欄位將出現。
現在,這就是為什么你的第二個棱鏡:
prism2 = stStep . _StateRun . _Just . stMMistake
不作業...
其實有兩個問題。一是stStep . _StateRun統籌兼顧。State_ (Int, Maybe Text)這不是一個Maybe值,所以它還不能用_Just棱鏡構圖。您想先選擇Maybe Text欄位,然后應用_Just棱鏡,所以您真正想要的更像是:
prism3 = stStep . _StateRun . stMMistake . _Just
This looks like it really should work, right? The stStep lens focuses on a StateStep, the _StateRun prism should focus only when a StateRun x y value is present, and the lens stMMistake ought to let you focus on the y :: Maybe Text, leaving the _Just to focus on the Text.
Unfortunately, this isn't how the prisms created with makePrisms work. The _StateRun prism focuses on a plain old tuple with unnamed fields, and those fields need to be further selected with _1, _2, etc., not stMMistake which is trying to select a named field.
In fact, if you take a careful look at stMMistake, you'll discover that -- all by itself -- it's a prism that takes a whole StateStep and focuses on the _stMMistake field part directly, without having to specify the constructor. So, you can actually use stMMistake in place of _StateStepRun . _2, and the following should work identically:
mMistake = st ^? stStep . _StateStepRun . _2 . _Just
mMistake = st ^? stStep . stMMistake . _Just
This isn't some fundamental theoretical property of lenses or anything. It's just the naming and typing convention used by makeLenses and makePrisms. With makeLenses, you create optics that focus on named fields of data structures. If there's only one constructor:
data Foo = Bar { _x :: Int, _y :: Double }
or if there are multiple constructors but the field is present in all constructors:
data Foo = Bar { _x :: Int, _y :: Double }
| Baz { _x :: Int, _z :: Char }
then the field optic (x in this example) is a lens that always focuses on that field. If there are multiple constructors and some have the field and some don't:
data Foo = Bar { _x :: Int, _y :: Double }
| Baz { _x :: Int, _z :: Char }
| Quux { _f :: Int -> Double }
then the field optic (x here) is a prism that focuses on the field, but only when it's present (i.e., when the value is a Bar or a Baz but not when it's a Quux).
On the other hand makePrisms always creates constructor prisms that focus on the fields as unnamed tuples, and those fields will need to be referenced with _1, _2, etc., rather than any names those fields happen to have within that constructor.
Maybe that answers your question?
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/434784.html
