Using Data.Map in monadic context

让人想犯罪 __ 提交于 2019-12-10 19:42:26

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!