Как сгладить IO (IO ())? - PullRequest
       80

Как сгладить IO (IO ())?

0 голосов
/ 27 декабря 2018

Я только изучаю Хаскель и монадные преобразователи, и я обнаружил, что у меня есть IO (IO ()), который я хотел бы превратить в просто IO ().Я уверен, что я делаю что-то не так, но не могу точно определить, где я заблудился.

Вот упрощенный пример того, что я пытаюсь сделать.Это сложный способ реализации echo, но он иллюстрирует проблему.

userInput :: Monad m => ReaderT (IO String) m (IO String)
userInput = ask

echo :: Monad m => ReaderT (IO String) m (IO ())
echo = userInput >>= \input ->  -- unwrap ReaderT to get an IO String
         input >>= (\s ->       -- unwrap IO String to get a String
           putStrLn s)          -- print out the String
         & return               -- rewrap into a ReaderT

main :: IO (IO ())              -- How to turn IO (IO ()) to IO ()?
main = runReaderT echo getLine

В моем реальном приложении у меня есть приложение Spock , которое отправляет HTTP-запросы на вышестоящий сервер.,Приложения Spock используют монадный стек преобразователя под названием SpockCtxT, и я хотел бы вставить ReaderT в стек, чтобы абстрагировать HTTP-запрос, чтобы я мог поменять его для фиктивной реализации в моих тестах.

По сути, идея заключается в стеке монадных преобразователей, в котором один из преобразователей дает IO, будь то HTTP-запрос или getLine.Я думаю об этом неправильно или есть какой-то способ сделать это?

Ответы [ 2 ]

0 голосов
/ 27 декабря 2018

Ответ на заданный вопрос - join :: IO (IO ()) -> IO ().Но ответ на вопрос, который, я думаю, вы должны задать, - liftIO :: IO () -> ReaderT (IO String) IO ().Например:

userInput :: MonadIO m => ReaderT (IO String) m String
userInput = ask >>= liftIO -- this liftIO eliminates your need for join

echo :: MonadIO m => ReaderT (IO String) m ()
echo = userInput >>= liftIO . putStrLn -- this liftIO is just so you can use putStrLn in ReaderT

main :: IO ()
main = runReaderT echo getLine

Создание монадических действий, которые возвращают монадические действия, а затем ручное объединение внутренних действий, в большинстве случаях игнорирует всю точку монадных преобразователей.Вместо того, чтобы иметь два слоя монадических действий, у вас должен быть один слой, который имеет версию преобразователя внешнего действия поверх внутреннего действия - то есть вместо работы с действиями ReaderT r Foo (IO a), которые требуют ручного связывания для обоихслой ReaderT r Foo и слой IO, вы должны работать с действиями ReaderT r (FooT IO) a, где только одна привязка обрабатывает эффекты чтения, foo и IO одновременно.

0 голосов
/ 27 декабря 2018

Используйте join.Он имеет тип подписи

join :: Monad m => m (m a) -> m a

, который специализируется на

join :: IO (IO ()) -> IO ()

, вы можете использовать hoogle , чтобы узнать это.Это инструмент командной строки.Мы можем искать по типу подписи:

hoogle "IO (IO ()) -> IO ()"

дает

Control.Monad join :: Monad m => m (m a) -> m a
Control.Composition (.$) :: Monad m => m (m a) -> m a
RIO join :: Monad m => m (m a) -> m a
Universum.Monad.Reexport join :: Monad m => m (m a) -> m a
Stack.Prelude join :: Monad m => m (m a) -> m a
Relude.Monad.Reexport join :: Monad m => m (m a) -> m a
Intro join :: Monad m => m (m a) -> m a
Hledger.Web.Import join :: Monad m => m (m a) -> m a
Data.Edison.Seq concat :: Sequence s => s (s a) -> s a
Data.Edison.Seq.Defaults concatUsingFoldr :: Sequence s => s (s a) -> s a
-- plus more results not shown, pass --count=20 to see more

, который имеет несколько функций, которые именно то, что вы хотите.

...