问题
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
.
回答1:
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
回答2:
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
来源:https://stackoverflow.com/questions/8575317/using-data-map-in-monadic-context