第一次使用lens。set并且over很容易,我認為這很簡單view:使用相同的方案來參考內部部分,但不提供新的值或函式。但是不。tst3 below gives the error below the code. 有誰知道發生了什么?
-- Testing lenses
tst1 = set (inner . ix 0 . w) 9 outer
tst2 = over (inner . ix 0 . w) ( 2) outer
tst3 = view (inner . ix 0 . w) outer -- this line errors out
* No instance for (Monoid Int) arising from a use of `ix'
* In the first argument of `(.)', namely `ix 0'
In the second argument of `(.)', namely `ix 0 . w'
In the first argument of `view', namely `(inner . ix 0 . w)'
這是一些簡化的代碼說明。
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
import Control.Lens
data Inner = Inner
{ _w :: Int
} deriving (Show, Eq)
data Outer = Outer
{ _inner :: [Inner]
} deriving Show
outer = Outer
[
Inner 0,
Inner 1,
Inner 2
]
makeLenses ''Inner
makeLenses ''Outer
tst1 = set (inner.ix 0.w) 999 outer
tst2 = over (inner.ix 0.w) ( 77) outer
tst3 = view (inner.ix 0.w) outer -- this errors out
我已經閱讀了view這些簡單的例子,就像我的一樣,
>>> let atom = Atom { _element = "C", _point = Point { _x = 1.0, _y = 2.0 } }
>>> view (point . x) atom
1.0
雖然我還沒有找到一個用 ix 索引內部結構的例子。
uj5u.com熱心網友回復:
問題是這ix 0是一個遍歷,而不是一個鏡頭,所以它不是關注第0 個元素,而是關注所有第 0 個元素的元素的集合。為什么這樣做?好吧,假設_inner是一個型別的串列[Inner],通常只有一個元素是第 0 個元素,但有時(如果串列為空),沒有元素是第 0 個元素。具有ix 0遍歷性說明了這種可能性。
由于ix 0是遍歷,它會“毒化”整個 optic inner . ix 0 . w。即使inner和w是鏡頭,其組合ix 0也“只是”一種遍歷。
現在,遍歷的“設定”或“過度”沒有問題。如果沒有第 0 個元素,則該操作不會執行任何操作。另一方面,查看這樣的遍歷有點問題。如果你希望得到一個元素,你可能不會得到一個。
出現錯誤訊息是因為view嘗試通過假設結果是 a 來處理遍歷Monoid。這允許它將遍歷的零個、一個或多個結果組合成一個相同型別的回傳值。這個功能有點深奧,這使得錯誤訊息相當混亂,但你已經習慣了看到它。
您可以做幾件事。您可以view用特殊的視圖運算子替換。通常的視圖運算子^.類似于view,因此它會生成相同的錯誤訊息:
-- parentheses are optional here, but included for clarity
tst4 = outer ^. (inner . ix 0 . w)
但是運營商^..,^?和^?!都會進行型別檢查:
tst5 = outer ^.. (inner . ix 0 . w)
tst6 = outer ^? (inner . ix 0 . w)
tst7 = outer ^?! (inner . ix 0 . w)
第一個(^..)將遍歷的所有(零個或多個)結果作為串列回傳。第二個(^?)將第一個結果回傳為Maybe,Nothing用于指示沒有結果。最后(^?!)一個直接回傳第一個結果,如果結果為零(例如head在空串列上使用),則會生成錯誤。
uj5u.com熱心網友回復:
ix 0不會產生鏡頭,而是產生遍歷。1
非正式地,鏡頭是一條“路徑”,它將最終達到一個值(如果您在假設的更大值內遵循它)。遍歷是通往零個或多個值的路徑。你可以set或view單一目標的一個鏡頭。并且您可以set遍歷零個或多個目標(這只是更新所有存在的目標,如果存在零個則為無操作)。但是view,遍歷的目標并不那么簡單。
如果view只是簡單地進行遍歷和外部結構,并給你目標值,那么它就會有問題。如果有多個目標,它應該如何決定回傳哪個?如果有零個目標,它不能回傳任何東西;它必須是部分的。它需要一種將零個或多個值壓縮為單個值的方法,因此它可以回傳該值。并且該Monoid課程正好提供了這樣做的設施;mempty如果根本沒有任何目標,并將<>多個值壓縮為一個值。因此view,遍歷2實際上需要Monoid對回傳的型別進行約束,這就是您收到關于No instance for (Monoid Int) arising from a use of `ix'.
如果不清楚,您通常可以組合(使用.)不同型別的光學元件(“鏡頭類事物”的通用術語,包括鏡頭、遍歷和其他幾個)。但結果具有兩個輸入中能力最差的能力。因此,即使您的inner和w是完整的鏡頭,將它們與由ix結果產生的遍歷組合成遍歷,而不是鏡頭。
但在這種情況下,您知道您正在使用ix. 特定型別的遍歷ix最終會產生零個或一個目標,而不是遍歷通常具有的零個或多個目標。所以你可以使用preview而不是view; 對于遍歷,它將產生一個Maybe包含遍歷Just的第一個目標,或者Nothing如果沒有任何目標。AMaybe正是型別系統在這里認為合適的,因為ix不能保證會有一個目標值,但不會超過一個。
以我的經驗,當我嘗試view做某事并得到這個Monoid實體錯誤時,它幾乎總是意味著我有一個不能保證結果的光學器件,我實際上應該使用preview它來獲得一個Maybe.
簡單的例子:
λ view (ix 1) [True, False]
<interactive>:16:7: error:
? No instance for (Monoid Bool) arising from a use of ‘ix’
? In the first argument of ‘view’, namely ‘(ix 1)’
In the expression: view (ix 1) [True, False]
In an equation for ‘it’: it = view (ix 1) [True, False]
λ preview (ix 1) [True, False]
Just False
it :: Maybe Bool
λ preview (ix 1) [True]
Nothing
it :: Maybe Bool
1 ix不能回傳鏡頭,因為它應該獲取一個索引,然后代表一個“路徑”進入任何可索引的結構(至少可以通過給定的索引型別來索引)。在ix 0,ix中還沒有看到你要使用它的特定結構,所以它無法保證該索引處有一個值;您甚至可以使用let i = ix 0然后i多次使用來窺視不同的結構!
2從技術上講,view由遍歷支持,因為它由折疊支持,并且所有遍歷都是折疊。折疊知道如何訪問零個或多個目標,但與遍歷不同,它不是“路徑”;它只是在不了解它們在結構中的背景關系的情況下訪問值(除了規范順序)。
在package的主要hackage 頁面lens上有一個方便但令人困惑的圖表,它試圖描述主要功能如何被各種光學風格“繼承”。這有點壓倒性......但比分散在許多不同模塊中的完整檔案要少。
您應該能夠看到在右上角我們Setter提供了setand over,它由 繼承Traversal。而在左上角,我們有view一個Monoid約束,它由Traversal. 在左中,我們有Getter一個view 沒有約束的版本Monoid,它被繼承Lens但不是被繼承Traversal。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/431375.html
