Использование Data.Map в монадическом контексте - PullRequest
3 голосов
/ 20 декабря 2011

Карта, на которой я работаю, имеет монадические ключи (типа IO Double).Мне нужно использовать findMax на этой карте.Могу ли я использовать liftM для этого?

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

Здесь f x имеет тип IO Double.

Ответы [ 2 ]

8 голосов
/ 20 декабря 2011

Не имеет смысла иметь значения типа IO в качестве ключей на карте.Значение типа IO t для некоторого типа t можно рассматривать как «программу», которая выдает значение типа t при каждом запуске: вы можете запускать его несколько раз и каждый раз, когда оно может производитьдругое значение.

Итак, вы, вероятно, хотите, чтобы вы сначала запустили "программу", чтобы получить некоторые результаты;тогда эти результаты могут стать ключами вашей карты.

Например, если у вас есть "программа"

f :: Int -> IO Int

, которая принимает целые и вычисляет, потенциально эффективно, целые числа, и вам нужночтобы выполнить ввод от [1 .. 10] для получения ключей вашей карты, вы можете выполнить следующее:

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

Это предполагает, что значения вычисляются из ключей функцией

g :: Int -> Int

createMap создает карту из целых чисел в целые числа;он возвращает его в IO -монаде, потому что то, какие ключи используются для заполнения карты, возможно, зависит от среды, в которой была запущена «программа» f.

Ваша проблема

В вашей ситуации это означает, что максимальное значение, которое вы хотите вычислить, должно быть получено и в IO -монаде:

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))

Построение карты постепенно

Карта, конечно, не должна быть создана за один раз, но также может быть построена постепенно:

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
5 голосов
/ 20 декабря 2011

Вы должны выполнить монадическое действие перед вставкой в ​​карту, например:

insertValue :: Value -> Map Key Value -> IO (Map Key Value)
insertValue value m = do
  key <- calculateKey value
  return $ Map.insert key value m

, где

calculateKey :: Value -> IO Key
...