1

In the code it works well as below

divide :: Int -> Int -> Either String Int
divide a 0 = Left "Error with 0"
divide a b = Right (a `div` b)

The code to be changed

Map.adjust (divide 3) "A" $ Map.fromList [("A",3),("B",0)]

The expected result should be :

Map.adjust (divide 3) "A" $ Map.fromList [("A",3),("B",0)] 
=>  Right $ Map.fromList [("A",1),("B",0)]   

Map.adjust (divide 3) "B" $ Map.fromList [("A",3),("B",0)]
=>  Left "Error with 0"

Or in general how to build a function like:

Map.adjust:: (a -> m a) -> k -> (Map k a) -> m (Map k a)

Thank you very much !

4
  • So for any Monad m, or is Either/Maybe sufficient? Commented Dec 8 at 14:51
  • 1
    The proposed type signature adjust:: (a -> m b) -> k -> (Map k a) -> m (Map k b) seems suspect, because it can change the type of the whole map even if the functions only modifies a single value inside it.
    – danidiaz
    Commented Dec 8 at 14:58
  • @willeM_VanOnsem I'm working with Either.. But it would be great to have more generic solution. Commented Dec 8 at 14:58
  • 1
    @danidiaz: yes, I assume it is a -> m a. Commented Dec 8 at 14:59

2 Answers 2

9

A function adjustM with type (a -> m a) -> k -> (Map k a) -> m (Map k a) can be defined on top of the already existing function alterF :: (Functor f, Ord k) => (Maybe a -> f (Maybe a)) -> k -> Map k a -> f (Map k a).

alterF is more general because, besides allowing for effects, it also lets you remove the targeted value, or insert it when the key doesn't already exist. (That's what those Maybes are for in its signature).

adjustM :: (Ord k, Applicative m) => (a -> m a) -> k -> Map k a -> m (Map k a)
adjustM f = Map.alterF (\ma -> case ma of
   -- No value? We do nothing.
   Nothing -> pure Nothing
   -- There's a value? We transform it.
   Just v -> Just <$> f v)

Or, more succinctly,

adjustM :: (Ord k, Applicative m) => (a -> m a) -> k -> Map k a -> m (Map k a)
adjustM f = Map.alterF (traverse f)
1
  • 2
    (Or more succinctly still, adjustM = Map.alterF . traverse)
    – amalloy
    Commented Dec 9 at 0:28
2

We can implement this by using insert instead:

updateM :: (Ord k, Applicative m) => (a -> m a) -> k -> Map k a -> m (Map k a)
updateM f k map = case (map !? k) of
  Just v -> flip (insert k) map <$> f v
  _ -> pure map

We thus first check if the key exists, if so run f v and then use that to insert the new value in the map.

This generates the expected result:

ghci> updateM (divide 3) "A" $ Data.Map.fromList [("A",3),("B",0)]
Right (fromList [("A",1),("B",0)])
ghci> updateM (divide 3) "B" $ Data.Map.fromList [("A",3),("B",0)]
Left "Error with 0"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.