Не имеет смысла иметь значения типа 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