Преобразование (a -> IO b) в IO (a -> b)

У меня есть несколько типов данных в контексте ввода-вывода, таких как:

a :: IO String
b :: IO FilePath
c :: String -> IO String

Я хочу собрать их все вместе в один объект данных, например:

data Configdata = Configdata String FilePath (String -> String)

Так что мне не нужно получать каждое значение отдельно из контекста ввода-вывода, а просто из IO Configdata,

Критическая точка, где у меня нет решения, - как я могу преобразовать String -> IO String в IO (String -> String), Hoogle не дает мне никаких функций, которые способны сделать это.

Я не уверен, возможно ли это даже невозможно, так как ввод функции возможно бесконечен.

У кого-то есть решение или объяснение, почему это невозможно? Я знаю, что использование списка вместо функции является опцией, но я бы предпочел использовать функцию, если это возможно.

1 ответ

Решение

На самом деле это невозможно. Рассмотрим функцию:

import Acme.Missiles

boo :: String -> IO String
boo "cute" = return "Who's a nice kitty?"
boo "evil" = launchMissiles >> return "HTML tags lea͠ki̧n͘g fr̶ǫm ̡yo​͟ur eye͢s̸ ̛l̕ik͏e liq​uid pain"

Теперь, если бы было возможно преобразовать это в IO (String -> String) пришлось бы выполнить все возможные IO действия для любого входа перед возвратом чистого String -> String функция. Даже если вы планируете использовать эту функцию только для наблюдения за котятами, это повлечет за собой ядерную катастрофу.

Тем не менее, вполне возможно сделать это для вашего конкретного приложения. В частности, если вы знаете, что функция будет вызываться только для заранее определенного набора строк, вы можете предварительно запросить их из IO и сохраните результаты в карте, которая затем может быть чисто проиндексирована.

import qualified Data.Map as Map

puh :: IO (String -> String)
puh = fmap ((Map.!) . Map.fromList) . forM ["cute"] $ \q -> do
       res <- boo q
       return (q, res)

Конечно, это может быть неосуществимо с точки зрения производительности.

Другие вопросы по тегам