2015-04-04 9 views
6

Ich bin auf der Suche nach einer Funktion, die wie foldlWithKey, aber in einer Monade gekapselt.faltlWithKey in monad

Ich würde erwarten, dass es Typen hat

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

aber Hoogle ist mir nicht mit dieser Art etwas zu geben.

Antwort

10

foldlWithKey ist schon sehr nahe, was Sie wollen. Wenn Sie sich auf a bis m a spezialisieren, werden Sie etwas haben, das mit Werten arbeitet, die in einer Monade eingekapselt sind.

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 -^ -} 

Wir der beiden m a s bekommen kann befreien Sie nicht wollen, mit >>= und 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 Lösung funktioniert sicher, aber es hat ein mögliches Problem: Er nistet >>= tief nach links s. Für viele (aber nicht alle!) Monaden kann dies einen Stapelvergrößerungseffekt ergeben, ähnlich wie bei Verwendung nicht-strikter foldl. Also werde ich eine andere Lösung vorstellen, die stattdessen s nach rechts nistet. Für Monaden wie IO sollte dies ermöglichen, dass die Aktion aus der Karte, wenn sie ausgeführt wird, träge konstruiert und konsumiert wird.

Diese Lösung ist wahrscheinlich etwas kniffliger, da sie eine richtige Falte verwendet, um die monadische Funktion aufzubauen, die schließlich den Startwert verbraucht. Zumindest hatte ich Schwierigkeiten, die Typen richtig zu machen.

Mit Ausnahme der Tastenbehandlung ist dies im Wesentlichen die gleiche Methode wie von 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

Äquivalent könnte man nicht einfach die Monade in 'ContT' für nur diese Funktion wickeln? – luqui

+0

@luqui Ich denke nicht, dass das ziemlich äquivalent ist, mit einer linken Falte würden Sie immer noch die gesamte links-verschachtelte Aktion aufbauen, bevor 'ContT' es konvertieren könnte. Insbesondere kann es beim Konsum nicht so faul sein. –