Nik*_*kov 19 haskell lenses haskell-lens
有这些进口:
> import Control.Lens
Control.Lens> import qualified Data.Map as Map
Run Code Online (Sandbox Code Playgroud)
和一个定义如下的地图值:
Control.Lens Map> let m = Map.fromList [('a', 1), ('c', 3), ('b', 2)]
Run Code Online (Sandbox Code Playgroud)
我可以像这样一个接一个地得到它的元素:
Control.Lens Map> view (at 'b') m
Just 2
Run Code Online (Sandbox Code Playgroud)
我想知道的是,有一组这样的键:
Control.Lens Map> import qualified Data.Set as Set
Control.Lens Map Set> let keys = Set.fromList ['d', 'c', 'b']
Run Code Online (Sandbox Code Playgroud)
如何构建这样一个getter(我猜),使用它我将能够获得匹配元素的集合(或列表):
Control.Lens Map Set> view (**???**) m
[3, 2]
Run Code Online (Sandbox Code Playgroud)
请注意,结果只包含2个元素,因为键不匹配'd'
.
ben*_*ofs 21
如果您只想要多个字段的getter,以下内容将起作用.
首先,你需要从镜头中创建一个Monoid实例的Accessor(该实例在 在HEAD,但尚未发布 已定义lens >= 4
,所以如果你正在使用旧版本的库,你只需要定义实例).
import Data.Monoid
import Control.Lens
instance Monoid r => Monoid (Accessor r a) where
mempty = Accessor mempty
mappend (Accessor a) (Accessor b) = Accessor $ a <> b
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用该实例将多个镜头/遍历组合成一个遍历:
>>> import qualified Data.Set as S
>>> import qualified Data.Map as M
>>> import Data.Foldable (foldMap)
>>> import Control.Lens
>>> let m = M.fromList [('a',1), ('b',2), ('c',3)]
>>> let k = S.fromList ['b','c','e']
>>> m ^.. foldMap at k
[Just 2,Just 3,Nothing]
>>> m ^.. foldMap ix k
[2,3]
Run Code Online (Sandbox Code Playgroud)
foldMap使用Monoid实例作为Accessor,使用Monoid实例作为函数.
我认为这是解决方案:
import Control.Applicative
import Control.Lens
import qualified Data.Map as M
import Data.Monoid hiding ((<>))
empty :: (Applicative f, Monoid a) => (b -> f b) -> (a -> f a)
empty _ _ = pure mempty
(<>)
:: (Applicative f, Monoid a)
=> ((b -> f b) -> (a -> f a))
-> ((b -> f b) -> (a -> f a))
-> ((b -> f b) -> (a -> f a))
(l1 <> l2) f a = mappend <$> (l1 f a) <*> (l2 f a)
Run Code Online (Sandbox Code Playgroud)
例:
>>> toListOf (at "A" <> at "B" <> at "C") (M.fromList [("A", 1), ("B", 2)])
[Just 1, Just 2, Nothing]
Run Code Online (Sandbox Code Playgroud)
这个想法是a Traversal
是一个幺半群.正确的解决方案需要newtyping Traversal
.
编辑:这是Monoid
所有newtype shenanigans 的正确实例:
import Control.Applicative
import Control.Lens
import qualified Data.Map as M
import Data.Monoid
import Data.Foldable
newtype Combinable f a b = Combinable { useAll :: (b -> f b) -> (a -> f a) }
instance (Applicative f, Monoid a) => Monoid (Combinable f a b) where
mempty = Combinable (\_ _ -> pure mempty)
mappend (Combinable l1) (Combinable l2)
= Combinable (\f a -> mappend <$> (l1 f a) <*> (l2 f a))
myMap :: M.Map String Int
myMap = M.fromList [("A", 1), ("B", 2)]
myLens :: Traversal' (M.Map String Int) (Maybe Int)
myLens = useAll $ foldMap (Combinable . at) ["A", "B", "C"]
Run Code Online (Sandbox Code Playgroud)
例:
>>> toListOf myLens myMap
[Just 1,Just 2, Nothing]
Run Code Online (Sandbox Code Playgroud)