【问题标题】:Using effect of Traversable [] and Applicative Maybe in lens libraryTraversable[]和Applicative Maybe在镜头库中的使用效果
【发布时间】:2020-06-25 16:13:44
【问题描述】:

我有以下结构:

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)]]

所以第三个条目基本上被忽略了。但我想要的是手术失败。

仅更新,如果所有地图都包含键“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"

我只是不知道如何组合它们

【问题讨论】:

    标签: haskell data-manipulation haskell-lens


    【解决方案1】:

    这里有几种替代方法,可以根据您喜欢的镜头进行查看;

    利用懒惰来延迟决定是否进行更改,

    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
    

    【讨论】:

      【解决方案2】:

      我看不到将其编写为镜头组合器的简单组合的方法,但这是一个您可以从头开始编写的遍历。如果每个映射都包含这样的键,则它应该遍历 "c" 键的所有值,否则不遍历任何值。

      我们可以从一个辅助函数开始,“也许”用一个新的键值更新映射,如果键不存在,则在 Maybe monad 中失败。出于显而易见的原因,我们希望允许更新发生在任意函子中。也就是说,我们想要一个函数:

      maybeUpdate :: (Functor f, Ord k) => k -> (v -> f v) -> Map k v -> Maybe (f (Map k v))
      

      那个签名清楚吗?我们检查密钥k如果找到键,我们将返回 Just 更新后的映射,其中键的对应值 vf 函子中更新。 否则,如果没有找到密钥,我们返回Nothing。我们可以很清楚地用 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
      

      或者,这些“单子动作”实际上只是函子动作,因此可以使用以下定义:

      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)]
      

      如果此遍历成功(返回Just 一个列表),则找到所有键,我们可以对f 应用操作进行排序:

      sequenceA <$> traverse (maybeUpdate k f) maps :: Maybe (f [Map k v])
      

      现在,如果遍历失败,我们只需使用maybe 返回原始列表:

      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。我从隐式身份应用程序中更愚蠢的“遍历”开始:

      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*)
      

      【讨论】:

      • pure for return and fromMaybe e for maybe e id
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-30
      • 1970-01-01
      • 2016-04-17
      • 1970-01-01
      • 2014-01-13
      • 1970-01-01
      • 2023-01-10
      相关资源
      最近更新 更多