Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Data.Map in monadic context

A map that I am operating on has monadic keys (of type IO Double). I need to use findMax on this map. Can I use liftM for this?

Map.findMax $ Map.fromList [(f x, "X"), (f y, "Y"), (f z, "Z")]

Here f x has type IO Double.

like image 287
atlantis Avatar asked Sep 03 '26 14:09

atlantis


1 Answers

It doesn't really make sense to have IO-typed values as keys in a map. A value of type IO t for some type t can be thought of as a "program" that produces a value of type t each time it is run: you can run it multiple times and every time it may produce a different value.

So, want you probably want is to first run the "program" to obtain some results; these results then can be the keys of your map.

For example, if you have a "program"

f :: Int -> IO Int

that takes integers and computes, potentially effectfully, integers, and you need to run in on inputs from [1 .. 10] to obtain the keys of your map, you could proceed as follows:

createMap :: IO (Map Int Int)
createMap = do
  keys <- mapM f [1 .. 10]
  return $ foldr (\k -> Map.insert k (g k)) Map.empty keys

This assumes that the values are computed from the keys by a function

g :: Int -> Int

createMap produces a map from integers to integers; it returns it in the IO-monad, because which keys are used to populate the map is possibly subject to the environment in which the "program" f was run.

Your Problem

In your situation, this means that the maximal value you want to compute, has to be produced in the IO-monad as well:

getMax :: Int -> Int -> Int -> IO (Double, String)
getMax x y z = do
  keys <- mapM f [x, y, z]
  let entries = zip keys ["X", "Y", "Z"]
  return (Map.findMax (Map.fromList entries))

Constructing the map incrementally

The map of course does not need to be created in one go, but can also be constructed incrementally:

f :: Int -> IO Int
f = ...

g :: Int -> Int
g = ...

createMap :: IO (Map Int Int)
createMap = do
  -- create the empty map
  let m0 = Map.empty

  -- insert an entry into the map
  k1 <- f 1
  let m1 = Map.insert k1 (g k1) m0

  -- extend the map with another entry
  k2 <- f 2
  let m2 = Map.insert k2 (g k2) m1

  -- return the map
  return m2
like image 92
Stefan Holdermans Avatar answered Sep 06 '26 15:09

Stefan Holdermans