Generic LensLike 像映射和遍历
Generic LensLike like mapped and traverse
假设我想为 MaybeT m a
:
的内容创建一个 "optic"
maybeTContents = _Wrapped .
something
. _Just
有这样的吗something
?
maybeTContents
例如 Traversal
当 m
是 []
时,
但只有 Setter
当 m
是 (->) Int
.
用法示例:
> MaybeT [Just 1, Nothing, Just 2] ^.. maybeTContents
[1, 2]
> runMaybeT (MaybeT (Just . ('e':)) & maybeTContents %~ ('H':)) "llo"
Just "Hello"
是的!首先要注意的是 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
统一的 setter?
但是,由于 []
和 Reader
都有仿函数实例,我们可以转向 绝对经典 setter mapped
、the setter heard around the world。 mapped
的类型为 mapped :: Functor f => Setter (f a) (f b) a b
– 当你有一个可用的仿函数实例时,mapped = sets fmap
是遵守所有 setter 法则的值。
我们可以在此处看到实际效果:
% 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,它服从我们想要的 setters 的那种法则,其中最重要的是 mapped . mapped . mapped
与(.)
。 lens
的其余部分很快就会跟进。
一种方法是制作您自己的 class,为您使用的类型提供正确的光学元件:
{-# 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
提供 setter 并为 []
等提供遍历
> 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
class 中包含 Profuctor
。您还可以编写一个允许索引镜头和遍历这种方式的版本。
基于以前答案的几个例子:
> 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
是对偶,对于 traverse
的 "go in other direction" 我们使用 collect
.
假设我想为 MaybeT m a
:
maybeTContents = _Wrapped .
something
. _Just
有这样的吗something
?
maybeTContents
例如 Traversal
当 m
是 []
时,
但只有 Setter
当 m
是 (->) Int
.
用法示例:
> MaybeT [Just 1, Nothing, Just 2] ^.. maybeTContents
[1, 2]
> runMaybeT (MaybeT (Just . ('e':)) & maybeTContents %~ ('H':)) "llo"
Just "Hello"
是的!首先要注意的是 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
统一的 setter?
但是,由于 []
和 Reader
都有仿函数实例,我们可以转向 绝对经典 setter mapped
、the setter heard around the world。 mapped
的类型为 mapped :: Functor f => Setter (f a) (f b) a b
– 当你有一个可用的仿函数实例时,mapped = sets fmap
是遵守所有 setter 法则的值。
我们可以在此处看到实际效果:
% 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,它服从我们想要的 setters 的那种法则,其中最重要的是 mapped . mapped . mapped
与(.)
。 lens
的其余部分很快就会跟进。
一种方法是制作您自己的 class,为您使用的类型提供正确的光学元件:
{-# 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
提供 setter 并为 []
等提供遍历
> 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
class 中包含 Profuctor
。您还可以编写一个允许索引镜头和遍历这种方式的版本。
基于以前答案的几个例子:
> 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
是对偶,对于 traverse
的 "go in other direction" 我们使用 collect
.