6

我正在寻找一个类似于 foldlWithKey 的函数,但封装在一个 monad 中。

我希望它有类型

Monad m => (a -> k -> b -> m a) -> a -> Map k b -> m a

但是 Hoogle 并没有给我这种类型的任何东西。

4

2 回答 2

10

foldlWithKey已经非常接近你想要的了。如果您专注am a您将拥有一些对封装在 monad 中的值进行操作的东西。

foldlWithKey :: (  a -> k -> b ->   a) ->   a -> Map k b ->   a
foldlWithKey :: (m a -> k -> b -> m a) -> m a -> Map k b -> m a
             {-  ^- you don't want these -^   -}

我们可以用和去掉m a你不想要的两个 s 。>>=return

foldlWithKeyM :: Monad m => (a -> k -> b -> m a) -> a -> Map k b -> m a
foldlWithKeyM f acc = foldlWithKey f' (return acc) 
    where
        f' ma k b = ma >>= \a -> f a k b
于 2015-04-04T16:28:31.197 回答
7

@Cirdec 的解决方案当然有效,但它有一个可能的问题:它嵌套>>=s 向左很深。对于许多(但不是全部!)monad,这可能会导致堆栈爆炸,类似于使用 non-strict 时foldl。所以我将提出一个不同的解决方案,它>>=改为向右嵌套。对于像IO这样的 monad,应该允许在执行时从 map 中懒惰地构造和消耗动作。

这个解决方案可能有点棘手,因为它使用正确的折叠来构建最终将消耗起始值的一元函数。至少我在正确选择类型时遇到了一些麻烦。

除了密钥处理之外,这与Data.Foldable.foldlM.

-- Pragma needed only to give f' a type signature for sanity.  Getting it 
-- right almost took a piece of mine until I remembered typed holes.
{-# LANGUAGE ScopedTypeVariables #-}

import Data.Map

foldlWithKeyM
  :: forall m a k b. Monad m => (a -> k -> b -> m a) -> a -> Map k b -> m a
foldlWithKeyM f start m = foldrWithKey f' return m $ start
  where
    f' :: k -> b -> (a -> m a) -> (a -> m a)
    f' k b a2mb a = f a k b >>= a2mb
于 2015-04-04T18:13:27.243 回答