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.
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.
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))
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
createMap-style solutions only work if you create the Map all at once rather than incrementally.createMap is one-shot; you can't add later data to the map in response to program input, changing state, etc. However, I've sent an edit in that shows how to abstracts the insertion out into insertNew (like my insertValue), which should hopefully make it clear how to achieve this.createMap didn't show how to add values to the map later on with the keys being produced by a monadic action. Admittedly, this is more a question of understanding how to structure monadic code than it is of Maps.You should execute the monadic action before inserting into the map, like this:
insertValue :: Value -> Map Key Value -> IO (Map Key Value)
insertValue value m = do
key <- calculateKey value
return $ Map.insert key value m
where
calculateKey :: Value -> IO Key
insertValue? It's the map you're inserting into, a Map Key Value — obviously, substitute your actual key and value types.process (Map.insert (calculateKey value) value m) with insertValue value m >>= process, or do { let m' = Map.insert (calculateKey value) m; ... } with do { m' <- insertValue value m; ... }, etc.Value argument, and produces an IO Key monadic action that, when executed, returns a value of type Key. I'm not sure what your code example means; key <- <$>f(x) isn't valid syntax.
IO akeys (for any a), because there is no Ord instance for them. What are you trying to achieve?