В этом конкретном случае пригодится монадный трансформатор MaybeT
. MaybeT
монадный трансформатор - это просто тип, определенный как:
newtype MaybeT m a = MaybeT {runMaybeT :: m (Maybe a)}
На самом деле трансформаторы, такие как MaybeT
, StateT
et c, легко доступны в Control.Monad.Trans.Maybe
, Control.Monad.Trans.State
.. В целях иллюстрации это может быть что-то вроде экземпляра Monad, как показано ниже;
instance Monad m => Monad (MaybeT m) where
return = MaybeT . return . Just
x >>= f = MaybeT $ runMaybeT x >>= g
where
g Nothing = return Nothing
g (Just x) = runMaybeT $ f x
, так как вы заметите, что функция monadi c f
принимает значение, которое находится в монаде Maybe
, которая сама находится в другой монаде (IO
в нашем случае). Функция f
делает свое дело и упаковывает результат обратно в MaybeT m a
.
Также есть класс MonadTrans
, где вы можете иметь некоторые общие функции, которые используются типами трансформаторов. Одним из них является lift
, который используется для поднятия значения в преобразователь в соответствии с определением этого конкретного экземпляра. Для MaybeT
он должен выглядеть следующим образом:
instance MonadTrans MaybeT where
lift = MaybeT . (liftM Just)
Позволяет выполнять вашу задачу с помощью монадных трансформаторов.
addInts :: MaybeT IO ()
addInts = do
lift $ putStrLn "Enter two integers.."
i <- lift getLine
guard $ test i
j <- lift getLine
guard $ test j
lift . print $ (read i :: Int) + (read j :: Int)
where
test = and . (map isDigit)
Поэтому при вызове типа
λ> runMaybeT addInts
Enter two integers..
1453
1571
3024
Just ()
Подвох Так как преобразователь монад также является членом класса типов Monad
, их можно вкладывать неопределенно долго и все же делать что-то под единичной нотацией do
.
Edit: ответ получает отрицательный ответ, но мне непонятно почему. Если с этим подходом что-то не так, пожалуйста, разработайте его, чтобы он помог людям, в том числе и мне, узнать что-то лучше.
Пользуясь возможностью присутствовать на сессии редактирования, я хотел бы добавить лучший код, так как я думаю, что Char
на основе test
ing может быть не лучшей идеей, так как она не будет принимать во внимание отрицательные Int
s. Итак, давайте попробуем использовать readMaybe
из пакета Text.Read
, пока мы работаем с типом Maybe
.
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Class (lift)
import Text.Read (readMaybe)
addInts :: MaybeT IO ()
addInts = do
lift $ putStrLn "Enter two integers.."
i <- lift getLine
MaybeT $ return (readMaybe i :: Maybe Int)
j <- lift getLine
MaybeT $ return (readMaybe j :: Maybe Int)
lift . print $ (read i :: Int) + (read j :: Int)
Полагаю, теперь он работает лучше ...
λ> runMaybeT addInts
Enter two integers..
-400
500
100
Just ()
λ> runMaybeT addInts
Enter two integers..
Not an Integer
Nothing