Actually, it makes no sense to have IO -type values โโas keys on the map. A value of type IO t for a certain type of t can be considered as a โprogramโ that produces a value of type t every time it starts: you can run it several times and every time it can create a different value.
So, you want you to probably want to run the โprogramโ first to get some results; these results can then be the keys of your card.
For example, if you have a "program"
f :: Int -> IO Int
which takes integers and calculates potentially effectively integers, and you need to run on inputs from [1 .. 10] to get the keys of your card, you can proceed as follows:
createMap :: IO (Map Int Int) createMap = do keys <- mapM f [1 .. 10] return $ foldr (\k -> Map.insert k (gk)) Map.empty keys
This assumes that the values โโare calculated using the keys using the function
g :: Int -> Int
createMap creates a map from integers to integers; he returns it to IO -monad, because which keys are used to populate the card may depend on the environment in which the โprogramโ was launched f .
Your problem
In your situation, this means that the maximum value you want to calculate must be created in IO -monad:
getMax :: Int -> Int -> Int -> IO (Double, String) getMax xyz = do keys <- mapM f [x, y, z] let entries = zip keys ["X", "Y", "Z"] return (Map.findMax (Map.fromList entries))
Building a display step by step
A map, of course, should not be created at a time, but can also be built gradually:
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