镜头库中Traversable[]和Applicative Maybe的使用效果
Using effect of Traversable [] and Applicative Maybe in lens library
我有以下结构:
y = [
fromList([("c", 1 ::Int)]),
fromList([("c", 5)]),
fromList([("d", 20)])
]
我可以用它来更新每个 "c":
y & mapped . at "c" . mapped %~ (+ 1)
-- [fromList [("c",2)], fromList [("c",6)], fromList [("d",20)]]
所以第三个条目基本上被忽略了。但我想要的是操作失败。
Only update, iff all the maps contain the key "c".
所以我想要:
y & mysteryOp
-- [fromList [("c",1)], fromList [("c",5)], fromList [("d",20)]]
-- fail because third entry does not contain "c" as key
我想我知道在这里要使用哪些函数了:
over
-- I want to map the content of the list
mapped
-- map over the structure and transform to [(Maybe Int)]
traverse
-- I need to apply the operation, which will avoid
at "c"
-- I need to index into the key "c"
就是不知道怎么组合
我看不出有什么方法可以把它写成一个简单的镜头组合器组合,但这是一个你可以从头开始写的遍历。如果每个映射都包含这样的键,它应该遍历 "c"
键的所有值,否则不遍历任何值。
我们可以从辅助函数开始 "maybe" 使用新键值更新映射,如果键不存在则 Maybe
monad 失败。由于显而易见的原因,我们希望允许更新发生在任意函子中。也就是我们要一个函数:
maybeUpdate :: (Functor f, Ord k) => k -> (v -> f v) -> Map k v -> Maybe (f (Map k v))
那个签名清楚吗?我们检查密钥 k
。 如果 找到了键,我们将 return Just
一个更新的映射,键的对应值 v
在 f
中更新函子。 否则,如果没有找到key,我们returnNothing
。我们可以用 monad 表示法很清楚地写这个,尽管如果我们只想使用 Functor f
约束,我们需要 ApplicativeDo
扩展:
maybeUpdate :: (Functor f, Ord k) => k -> (v -> f v) -> Map k v -> Maybe (f (Map k v))
maybeUpdate k f m = do -- in Maybe monad
v <- m ^. at k
return $ do -- in "f" functor
a <- f v
return $ m & at k .~ Just a
或者,这些 "monadic actions" 实际上只是仿函数操作,因此可以使用此定义:
maybeUpdate' k f m =
m ^. at k <&> \v -> f v <&> \a -> m & at k .~ Just a
这是困难的部分。现在,遍历非常简单。我们从签名开始:
traverseAll :: (Ord k) => k -> Traversal' [Map k v] v
traverseAll k f maps =
想法是,此遍历首先使用 maybeUpdate
帮助程序遍历 Maybe
应用程序上的地图列表:
traverse (maybeUpdate k f) maps :: Maybe [f (Map k v)]
如果此遍历成功(returns Just
一个列表),则找到所有键,我们可以对 f
应用操作进行排序:
sequenceA <$> traverse (maybeUpdate k f) maps :: Maybe (f [Map k v])
现在,如果遍历失败,我们就用maybe
到return原来的链表:
traverseAll k f maps = maybe (pure maps) id (sequenceA <$> traverse (maybeUpdate k f) maps)
现在,有:
y :: [Map String Int]
y = [
fromList([("c", 1 ::Int)]),
fromList([("c", 5)]),
fromList([("d", 20)])
]
y2 :: [Map String Int]
y2 = [
fromList([("c", 1 ::Int)]),
fromList([("c", 5)]),
fromList([("d", 20),("c",6)])
]
我们有:
> y & traverseAll "c" %~ (1000*)
[fromList [("c",1)],fromList [("c",5)],fromList [("d",20)]]
> y2 & traverseAll "c" %~ (1000*)
[fromList [("c",1000)],fromList [("c",5000)],fromList [("c",6000),("d",20)]]
完全披露:我无法从头开始构建这样的 traverseAll
。我从隐式身份应用程序中更愚蠢的 "traversal" 开始:
traverseAllC' :: (Int -> Int) -> [Map String Int] -> [Map String Int]
traverseAllC' f xall = maybe xall id (go xall)
where go :: [Map String Int] -> Maybe [Map String Int]
go (x:xs) = case x !? "c" of
Just a -> (Map.insert "c" (f a) x:) <$> go xs
Nothing -> Nothing
go [] = Just []
一旦我弄好了 运行,我简化了它,使 Identity
明确:
traverseAllC_ :: (Int -> Identity Int) -> [Map String Int] -> Identity [Map String Int]
并将其转换为通用应用程序。
无论如何,这是代码:
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE RankNTypes #-}
import Data.Map (Map, fromList)
import Control.Lens
y :: [Map [Char] Int]
y = [
fromList([("c", 1 ::Int)]),
fromList([("c", 5)]),
fromList([("d", 20)])
]
y2 :: [Map [Char] Int]
y2 = [
fromList([("c", 1 ::Int)]),
fromList([("c", 5)]),
fromList([("d", 20),("c",6)])
]
traverseAll :: (Ord k) => k -> Traversal' [Map k v] v
traverseAll k f maps = maybe (pure maps) id (sequenceA <$> traverse (maybeUpdate k f) maps)
maybeUpdate :: (Functor f, Ord k) => k -> (v -> f v) -> Map k v -> Maybe (f (Map k v))
maybeUpdate k f m = do
v <- m ^. at k
return $ do
a <- f v
return $ m & at k .~ Just a
maybeUpdate' :: (Functor f, Ord k) => k -> (v -> f v) -> Map k v -> Maybe (f (Map k v))
maybeUpdate' k f m =
m ^. at k <&> \v -> f v <&> \a -> m & at k .~ Just a
main = do
print $ y & traverseAll "c" %~ (1000*)
print $ y2 & traverseAll "c" %~ (1000*)
根据您的喜好,这里有几种替代方法;
利用懒惰来延迟决定是否进行更改,
f y = res
where (All c, res) = y
& each %%~ (at "c" %%~ (Wrapped . is _Just &&& fmap (applyWhen c succ)))
或预先决定是否进行更改,
f' y = under (anon y $ anyOf each (nullOf $ ix "c")) (mapped . mapped . ix "c" +~ 1) y
我有以下结构:
y = [
fromList([("c", 1 ::Int)]),
fromList([("c", 5)]),
fromList([("d", 20)])
]
我可以用它来更新每个 "c":
y & mapped . at "c" . mapped %~ (+ 1)
-- [fromList [("c",2)], fromList [("c",6)], fromList [("d",20)]]
所以第三个条目基本上被忽略了。但我想要的是操作失败。
Only update, iff all the maps contain the key "c".
所以我想要:
y & mysteryOp
-- [fromList [("c",1)], fromList [("c",5)], fromList [("d",20)]]
-- fail because third entry does not contain "c" as key
我想我知道在这里要使用哪些函数了:
over
-- I want to map the content of the list
mapped
-- map over the structure and transform to [(Maybe Int)]
traverse
-- I need to apply the operation, which will avoid
at "c"
-- I need to index into the key "c"
就是不知道怎么组合
我看不出有什么方法可以把它写成一个简单的镜头组合器组合,但这是一个你可以从头开始写的遍历。如果每个映射都包含这样的键,它应该遍历 "c"
键的所有值,否则不遍历任何值。
我们可以从辅助函数开始 "maybe" 使用新键值更新映射,如果键不存在则 Maybe
monad 失败。由于显而易见的原因,我们希望允许更新发生在任意函子中。也就是我们要一个函数:
maybeUpdate :: (Functor f, Ord k) => k -> (v -> f v) -> Map k v -> Maybe (f (Map k v))
那个签名清楚吗?我们检查密钥 k
。 如果 找到了键,我们将 return Just
一个更新的映射,键的对应值 v
在 f
中更新函子。 否则,如果没有找到key,我们returnNothing
。我们可以用 monad 表示法很清楚地写这个,尽管如果我们只想使用 Functor f
约束,我们需要 ApplicativeDo
扩展:
maybeUpdate :: (Functor f, Ord k) => k -> (v -> f v) -> Map k v -> Maybe (f (Map k v))
maybeUpdate k f m = do -- in Maybe monad
v <- m ^. at k
return $ do -- in "f" functor
a <- f v
return $ m & at k .~ Just a
或者,这些 "monadic actions" 实际上只是仿函数操作,因此可以使用此定义:
maybeUpdate' k f m =
m ^. at k <&> \v -> f v <&> \a -> m & at k .~ Just a
这是困难的部分。现在,遍历非常简单。我们从签名开始:
traverseAll :: (Ord k) => k -> Traversal' [Map k v] v
traverseAll k f maps =
想法是,此遍历首先使用 maybeUpdate
帮助程序遍历 Maybe
应用程序上的地图列表:
traverse (maybeUpdate k f) maps :: Maybe [f (Map k v)]
如果此遍历成功(returns Just
一个列表),则找到所有键,我们可以对 f
应用操作进行排序:
sequenceA <$> traverse (maybeUpdate k f) maps :: Maybe (f [Map k v])
现在,如果遍历失败,我们就用maybe
到return原来的链表:
traverseAll k f maps = maybe (pure maps) id (sequenceA <$> traverse (maybeUpdate k f) maps)
现在,有:
y :: [Map String Int]
y = [
fromList([("c", 1 ::Int)]),
fromList([("c", 5)]),
fromList([("d", 20)])
]
y2 :: [Map String Int]
y2 = [
fromList([("c", 1 ::Int)]),
fromList([("c", 5)]),
fromList([("d", 20),("c",6)])
]
我们有:
> y & traverseAll "c" %~ (1000*)
[fromList [("c",1)],fromList [("c",5)],fromList [("d",20)]]
> y2 & traverseAll "c" %~ (1000*)
[fromList [("c",1000)],fromList [("c",5000)],fromList [("c",6000),("d",20)]]
完全披露:我无法从头开始构建这样的 traverseAll
。我从隐式身份应用程序中更愚蠢的 "traversal" 开始:
traverseAllC' :: (Int -> Int) -> [Map String Int] -> [Map String Int]
traverseAllC' f xall = maybe xall id (go xall)
where go :: [Map String Int] -> Maybe [Map String Int]
go (x:xs) = case x !? "c" of
Just a -> (Map.insert "c" (f a) x:) <$> go xs
Nothing -> Nothing
go [] = Just []
一旦我弄好了 运行,我简化了它,使 Identity
明确:
traverseAllC_ :: (Int -> Identity Int) -> [Map String Int] -> Identity [Map String Int]
并将其转换为通用应用程序。
无论如何,这是代码:
{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE RankNTypes #-}
import Data.Map (Map, fromList)
import Control.Lens
y :: [Map [Char] Int]
y = [
fromList([("c", 1 ::Int)]),
fromList([("c", 5)]),
fromList([("d", 20)])
]
y2 :: [Map [Char] Int]
y2 = [
fromList([("c", 1 ::Int)]),
fromList([("c", 5)]),
fromList([("d", 20),("c",6)])
]
traverseAll :: (Ord k) => k -> Traversal' [Map k v] v
traverseAll k f maps = maybe (pure maps) id (sequenceA <$> traverse (maybeUpdate k f) maps)
maybeUpdate :: (Functor f, Ord k) => k -> (v -> f v) -> Map k v -> Maybe (f (Map k v))
maybeUpdate k f m = do
v <- m ^. at k
return $ do
a <- f v
return $ m & at k .~ Just a
maybeUpdate' :: (Functor f, Ord k) => k -> (v -> f v) -> Map k v -> Maybe (f (Map k v))
maybeUpdate' k f m =
m ^. at k <&> \v -> f v <&> \a -> m & at k .~ Just a
main = do
print $ y & traverseAll "c" %~ (1000*)
print $ y2 & traverseAll "c" %~ (1000*)
根据您的喜好,这里有几种替代方法;
利用懒惰来延迟决定是否进行更改,
f y = res
where (All c, res) = y
& each %%~ (at "c" %%~ (Wrapped . is _Just &&& fmap (applyWhen c succ)))
或预先决定是否进行更改,
f' y = under (anon y $ anyOf each (nullOf $ ix "c")) (mapped . mapped . ix "c" +~ 1) y