假设我想为MaybeT m a
的内容创建一个“光学组件”
maybeTContents = _Wrapped .something. _Just
有这样的something
吗?
例如,当m
为[]
时,maybeTContents
将为Traversal
,但当m
为(->) Int
时,仅为Setter
。
示例用法:
> MaybeT [Just 1, Nothing, Just 2] ^.. maybeTContents
[1, 2]
> runMaybeT (MaybeT (Just . ('e':)) & maybeTContents %~ ('H':)) "llo"
Just "Hello"
发布于 2016-06-29 22:09:14
要做到这一点,一种方法是制作自己的类,为您使用的类型提供正确的光学效果:
{-# 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"
您还可以为MaybeT
和ReaderT
编写实例
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
。您也可以编写一个版本,允许以这种方式索引镜头和遍历。
发布于 2016-06-23 00:45:24
是!首先要注意的是,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
exist和Setter' (Reader u) u
统一的setter
但是,由于[]
和Reader
都有函数器实例,我们可以求助于一个绝对经典的设置器mapped
,the 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
实例,为了说明setter是如何工作的,我只能生成两个全新的类型-- A
和B
。
我认为这个问题很好,因为它是lens
包背后动机的核心。给定来自Traversable
世界的fmapDefault
,您可以将遍历修复为Identity
来编写over
。然后,您可以编写over
、sets
的倒数,这样over . sets = id
和sets . over = id
就可以了。然后,我们不得不得出结论,mapped = sets fmap
是一种自然的setter,它遵循我们想要的setter的规则,其中最重要的是mapped . mapped . mapped
与(.)
的组合。lens
的其余部分很快就会跟进。
发布于 2016-06-29 23:23:02
以下是基于前面答案的几个示例:
> 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
,对于Setter
:collect
(或mapped
)。
不幸的是,Traversable
和Distributive
并不具备所有的实例:(->) r
不是Traversable
,Const
也不是Distributive
(而且它们也不可能是AFAICS)。
如果你仔细想想,你会发现这是有道理的。Traversal
和Distributive
是二重奏,因此我们使用collect
来表示traverse
的“另一个方向”。
https://stackoverflow.com/questions/37966128
复制相似问题