2015-01-24 10 views
7

Следующая функция f попытки прочитать Int дважды, используя IO (Maybe Int) функцию дважды, но «короткие замыкания» исполнение после того, как успешно читает один Int:Рефакторинг «лестничный» с корпусом из `значений Maybe` в` IO` код

readInt :: IO (Maybe Int) 

f :: IO (Maybe Int) 
f = do 
    n1 <- readInt 
    case n1 of 
     Just n' -> return (Just n') 
     Nothing -> do 
     n2 <- readInt 
     case n2 of 
       Just n' -> return (Just n') 
       Nothing -> return Nothing 

Есть ли хороший способ реорганизовать этот код? Это было бы получить очень волосатый, если я продлил его до трех попыток ...

(Мой мыслительный процесс: Видя это «лестничный» говорит мне, что, может быть, я должен использовать в Monad экземпляр Maybe, но так как это уже в IO монады я бы тогда использовать MaybeT (?). Тем не менее, мне нужно только один из readInt, чтобы добиться успеха, так что поведение Maybe монады из erroring на первой Nothing бы здесь не так ...)

ответ

8

Вы можете использовать MaybeT и экземпляр MonadPlus для использования msum:

f :: MaybeT IO Int 
f = msum [readInt, readInt, readInt] 
+0

Возможно, уточните в пользу других, что он должен был бы провести вычисление 'IO (Maybe Int)' в конструкторе 'MaybeT' –

1

Прежде всего,

n2 <- readInt 
case n2 of 
     Just n' -> return (Just n') 
     Nothing -> return Nothing 

действительно просто readInt. Вы выделяете значение Maybe, чтобы собрать то же самое.

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

f = maybe readInt (return . Just) =<< readInt 
4

Вам нужен альтернативный экземпляр для MaybeT:

instance (Functor m, Monad m) => Alternative (MaybeT m) where 
    empty = mzero 
    (<|>) = mplus 

instance (Monad m) => MonadPlus (MaybeT m) where 
    mzero = MaybeT (return Nothing) 
    mplus x y = MaybeT $ do 
     v <- runMaybeT x 
     case v of 
      Nothing -> runMaybeT y 
      Just _ -> return v 

И.Э. вычислите первый аргумент и верните значение, если оно равно Just, иначе вычислите второй аргумент и верните значение.

Код:

import Control.Applicative 
import Control.Monad 
import Control.Monad.Trans.Maybe 
import Text.Read 

readInt :: MaybeT IO Int 
readInt = MaybeT $ readMaybe <$> getLine 

main = runMaybeT (readInt <|> readInt) >>= print 
1

Другой способ сделать это с iterative monad transformer из free пакета.

import Control.Monad.Trans.Iter (untilJust,retract,cutoff,IterT) 

readInt :: IO (Maybe Int) 
readInt = undefined 

f' :: IterT IO Int 
f' = untilJust readInt 

f :: IO (Maybe Int) 
f = (retract . cutoff 2) f' 

Где cutoff определяет максимальное число повторных попыток.

Преимущество этого подхода заключается в том, что вы можете легко interleave other repeated actions с f', благодаря MonadPlus экземпляру IterT. Например, действия регистрации или действия ожидания.

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

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