2016-06-22 21 views
19

假設我想創建一個 「光」 爲MaybeT m a內容:通用透鏡狀像映射並遍歷

maybeTContents = _Wrapped .something. _Just

是否有這樣的something

maybeTContents將例如爲Traversalm[], 但只有當Setterm(->) Int

用法示例:

+0

@haoformayor關於標題編輯 - 'MaybeT'只是我正在尋找的一個例子。希望新的標題更好地解釋我在找什麼 – yairchu

+2

另請參閱https://github.com/ekmett/lens/wiki/Varying-lens-properties-by-instance – phadej

回答

5

一種方法是讓自己的類,讓右視覺的類型您使用:

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE RankNTypes    #-} 

class Ocular f t where 
    optic :: LensLike f (t a) (t b) a b 

instance Settable f => Ocular f ((->) a) where 
    optic = mapped 

instance Functor f => Ocular f Identity where 
    optic = _Wrapped 

instance Applicative f => Ocular f [] where 
    optic = traversed 

instance Applicative f => Ocular f Maybe where 
    optic = _Just 

這將給予(->) s二傳手和[]

遍歷
> let maybeTContents = _Wrapped . optic . _Just 
> MaybeT [Just 1, Nothing, Just 2] ^.. maybeTContents 
[1,2] 
> runMaybeT (MaybeT (Just . ('e':)) & maybeTContents %~ ('H':)) "llo" 
Just "Hello" 

你也可以寫一個實例MaybeTReaderT

instance (Applicative f, Ocular f m) => Ocular f (MaybeT m) where 
    optic = _Wrapped . optic . _Just 


instance (Ocular f m, Settable f) => Ocular f (ReaderT r m) where 
    optic = _Wrapped . mapped . optic 

> MaybeT [Just 1, Nothing, Just 2] ^.. optic 
[1,2] 
> runReaderT (ReaderT (\r -> [r,r+1]) & optic *~ 2) 1 
[2,4] 

請注意,Identity的情況僅爲Lens,而非Iso。爲此,您需要在Ocular類中包含Profuctor。您也可以編寫一個允許索引鏡頭和遍歷的版本。

+1

這個更好的版本是爲約束使用一個類關聯類型,w /一個證明你的光學元件至少是一些最小光學級別的元件。很難做400字符評論,我無法格式化代碼,但'類HasFoo e其中{類型T e :: * - >約束; foo':: T e f => LensLike'f e Foo; fooIsalens :: T e f: - Functor f}'然後你可以通過操作約束來定義'foo :: HasFoo e => Lens'e Foo'。在單形態的情況下,你可以像以前一樣繼續進行操作,但現在你可以合理地使用'foo'參數化地將每個中間'f'漏入你的簽名中。 –

+0

@EdwardKMETT - 這是我到目前爲止: https://github.com/lamdu/lamdu/blob/master/bottlelib/Data/Traversable/Generalized.hs 幾乎和你所描述的一樣,但用'Functor f'在鏡頭的約束,而不是'fooIsALens' – yairchu

+1

@愛德華凱特我已經去了,這是你的意思? http://lpaste.net/168558你不能使用類型同義詞來獲得類似的效果(即'SetFoo = HasFoo Identity')嗎? – cchalmers

8

是的!首先要注意的是something必須有Setter(並且不失一般性,Setter')。至於什麼類型讓我們使用洞。

maybeTContents :: Setter' (MaybeT m a) a 
maybeTContents = 
    _Wrapped . _ . _Just 

GHC告訴我們,它想要類型Settable f => (Maybe a -> f (Maybe a)) -> (m (Maybe a) -> f (m (Maybe a))的洞。

隨着Hackage之旅,我們認識到這種類型爲Setter' (m (Maybe a)) (Maybe a)。因此,修正u ~ Maybe a,我們可以更一般地重新提出這個問題:是否存在一個與Setter' [u] u存在和Setter' (Reader u) u相一致的存在?

但是,因爲這兩個[]Reader有仿函數情況下,我們可以把一個二傳手mappedthe setter heard around the world絕對經典mapped的類型爲mapped :: Functor f => Setter (f a) (f b) a b - 事實證明,當您有一個可用的函數實例時,mapped = sets fmap是遵從所有設置法則的值。

我們可以在這裏的行動看到這一點:

% stack ghci 
GHCi, version 7.10.3: http://www.haskell.org/ghc/ :? for help 
Ok, modules loaded: none. 
λ> import Control.Lens 
λ> import Control.Monad.Trans.Maybe 
λ> import Control.Monad.Trans.Reader 
λ> MaybeT [Just 1, Nothing, Just 2, Nothing, Just 3] & _Wrapped . mapped . _Just .~ 100 
MaybeT [Just 100,Nothing,Just 100,Nothing,Just 100] 
λ> data A = A 
λ> data B = B 
λ> :t MaybeT (ReaderT (\r -> Identity (Just A))) 
MaybeT (ReaderT (\r -> Identity (Just A))) 
    :: MaybeT (ReaderT r Identity) A 
λ> :t MaybeT (ReaderT (\r -> Identity (Just A))) & _Wrapped . mapped . _Just .~ B 
MaybeT (ReaderT (\r -> Identity (Just A))) & _Wrapped . mapped . _Just .~ B 
    :: MaybeT (ReaderT r Identity) B 

,因爲那裏可ReaderT沒有Show比如我能做的最好說明的是,二傳手工作是產生兩個品牌spankin'新類型AB

這個問題很好,我認爲它是lens包背後動機的核心。從Traversable世界給定fmapDefault,您可以修復遍歷爲Identity來編寫over。然後你可以寫出over,sets的倒數,例如over . sets = idsets . over = id。然後我們不得不得出結論,mapped = sets fmap是一個自然的設置者,它遵循我們想要的設置者的法則,其中最重要的是mapped . mapped . mapped(.)合成。緊接着是lens的其餘部分。要做到這一點

+1

使用'something = mapped',你可以做' MaybeT [只有1,沒有,只有2]^.. maybeTContents'? (剛剛更新我的問題有這個例子) – yairchu

+1

@yairchu是的。所有制定者也是遍歷的。 (請參閱鏡頭Hackage頁面上那個巨大的UML圖。) – hao

+1

它似乎不起作用。 ':t MaybeT [只'a',沒什麼,只'b']^.. _包裹。映射。 _Just'導致錯誤'No set for(Settable(Const(Data.Monoid.Endo [Char])))由於使用'mapped''而產生... – yairchu

1

基於以前的答案几個例子:

> MaybeT [Just 1, Nothing, Just 2] ^.. _Wrapped . traverse . _Just 
[1,2] 
> runMaybeT (MaybeT (Just . ('e':)) & _Wrapped . collect . _Just %~ ('H':)) "llo" 
Just "Hello" 

即對於Traversal/Fold,我們使用traverse,對於Settercollect(或mapped)。

不幸的是TraversableDistributive沒有所有實例: (->) rTraversableConstDistributive(他們不能,AFAICS)。

如果你想到這一點,你會發現它是有道理的。 TraversalDistributive是雙向的,我們使用collect來「去其他方向」traverse