2015-04-04 4 views
6

Я ищу функцию, подобную foldlWithKey, но инкапсулированную в монаду.foldlWithKey in monad

Я бы ожидать, что это иметь тип

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

но Hoogle не дает мне ничего с этим типом.

ответ

10

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 
7

@ решение 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 
+0

Эквивалентно вы могли бы не просто обернуть монаду в 'ContT' только для этой функции? – luqui

+0

@luqui Я не думаю, что это вполне эквивалентно, с левой складкой вы все равно будете * наращивать * все левое вложенное действие до того, как «ContT» сможет его преобразовать. В частности, он не может быть ленивым при потреблении. –

Смежные вопросы