2016-08-18 14 views
4

У меня есть несколько типов данных в контексте IO, как:Трансформация (а -> IO б) в IO (а -> б)

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

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

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

Так что я не должен получить значение каждого за свое собственное из контекста IO, но только из IO Configdata.

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

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

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

+14

Это на самом деле несовместимо. В 'String -> IO String' вычисление IO может зависеть от аргумента в' IO (String -> String) 'оно не может. – Bergi

+1

@ Берги это прекрасно. Это ключевое различие между Аппликативным и Монадом. – user2297560

ответ

10

Действительно, это невозможно. Рассмотрим функцию:

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), он должен выполнить all possible IO actions for any input перед возвратом чистой String -> String функции. IOW, даже если вы только планируете использовать эту функцию для целей наблюдения за котятами, это повлечет за собой ядерный холокост.

Тем не менее, вполне возможно это сделать для вашего конкретного приложения. В частности, если вы знаете, что функция будет вызвана только для заданного набора строк, вы можете предварительно запросить их у 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) 

Конечно, это может быть неосуществимым.

+2

Если имеется заданный набор строк, вероятно, имеет смысл сделать небольшой настраиваемый тип с одним значением для каждой строки в наборе. Тогда пакет юниверса может дать вам ['sequenceA :: (Foo -> IO String) -> IO (Foo -> String)'] (http://hackage.haskell.org/package/universe-1.0/docs/Data -Universe-Instances-Reverse.html # t: Traversable), в котором описано поведение, создаваемое в нем «Карта» и индексирование. –

 Смежные вопросы

  • Нет связанных вопросов^_^