Я ищу функцию, подобную foldlWithKey, но инкапсулированную в монаду.foldlWithKey in monad
Я бы ожидать, что это иметь тип
Monad m => (a -> k -> b -> m a) -> a -> Map k b -> m a
но Hoogle не дает мне ничего с этим типом.
Я ищу функцию, подобную foldlWithKey, но инкапсулированную в монаду.foldlWithKey in monad
Я бы ожидать, что это иметь тип
Monad m => (a -> k -> b -> m a) -> a -> Map k b -> m a
но Hoogle не дает мне ничего с этим типом.
foldlWithKey
уже очень близко к тому, что вы хотите. Если вы специализируетесь на a
до m a
, у вас будет что-то, что действует на значения, инкапсулированные в монаду.
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
с вы не хотите с >>=
и 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
@ решение Cirdec, безусловно, работает, но есть возможная проблема: Гнездится >>=
сек глубоко влево. Для многих (но не всех!) Монад, это может привести к разрыву стека, аналогичному при использовании нестрогого foldl
. Поэтому я представлю другое решение, которое вместо этого накладывает >>=
. Для монад, таких как IO
, это должно позволить, чтобы действие было построено и потреблялось лениво с карты по мере ее выполнения.
Это решение, вероятно, немного сложнее, так как оно использует вправо fold, чтобы создать монадическую функцию, которая в конечном итоге будет потреблять начальное значение. По крайней мере, у меня были проблемы с правильными типами.
За исключением манипуляции с ключами, это по существу тот же метод, который используется 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
Эквивалентно вы могли бы не просто обернуть монаду в 'ContT' только для этой функции? – luqui
@luqui Я не думаю, что это вполне эквивалентно, с левой складкой вы все равно будете * наращивать * все левое вложенное действие до того, как «ContT» сможет его преобразовать. В частности, он не может быть ленивым при потреблении. –