2015-10-15 1 views
1

Иногда мне нужно использовать несколько вложенных MonadTrans. Например, я бы поставил MaybeT внутренности ExceptT, чтобы подражать continue и break в императивном программировании:Есть ли лучший способ, чем писать `lift` перед каждым `IO` в вложенном` MonadTrans`?

runExceptT . forM [1..] $ \ _ -> runMaybeT $ do 
    ... 
    mzero -- this mimics continue 
    lift $ throwE "..." -- this mimics break 
    lift . lift $ putStrLn "Hello!" 
    ... 

Однако, как выше показывает код, каждый раз, когда мне нужно сделать любое IO внутри этого «искусственный петля ", мне нужно поставить уродливый lift . lift перед этим. Представьте себе, если у меня есть еще более сложные гнездования и много операций IO, это быстро становится анонимным. Как сделать код более чистым и более кратким?

+4

Работает ли 'liftIO'? – ErikR

+1

Обратите внимание, что в некоторых случаях более эффективно поднимать вещи в блоках. 'lift $ do {x; y; z}' должно быть предпочтительнее: 'do {lift x; подъем y; подъем z} '. Если это окажется проблемой в узком цикле, вам может понадобиться 'lift $ do {blah; лифт. – dfeuer

+0

Возможный дубликат [стека штанов Flatten] (http://stackoverflow.com/questions/32551152/flatten-monad-stack) – jakubdaniel

ответ

6

Для конкретного случая IO, вы можете использовать liftIO :: MonadIO m => IO a -> m a чтобы поднять IO действие через произвольно глубокий стек монада трансформатора:

liftIO $ putStrLn "Hello" 

Это выражение имеет тип MonadIO m => m(). Любой стек трансформатора, содержащий экземпляр MonadIO, где-то внутри него должен иметь экземпляр MonadIO, поэтому это работает для стека любой глубины.

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