2015-01-24 11 views
7

La seguente funzione f tentativi di leggere un Int due volte utilizzando una funzione IO (Maybe Int) due volte, ma “cortocircuiti” esecuzione dopo aver letto con successo uno Int:Refactoring “gradinata” con il caso di `` valori Maybe` in codice 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 

C'è un buon modo per refactoring questo codice? Questo otterrebbe molto peloso se ho esteso a tre tentativi ...

(Il mio processo di pensiero: Vedendo questo “gradinata” mi dice che forse dovrei usare l'istanza Monad di Maybe, ma poiché questa è già in IO Monade , avrei quindi dovuto usare MaybeT (?). Tuttavia, ho solo bisogno di una del readInt per avere successo, quindi il comportamento del Maybe monade erroring sul primo Nothing sarebbe sbagliato qui ...)

risposta

8

È possibile utilizzare MaybeT e l'istanza MonadPlus per utilizzare msum:

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

Forse chiarire a beneficio degli altri che avrebbe dovuto completare il calcolo 'IO (Forse Int)' nel costruttore 'MaybeT' –

1

Prima di tutto,

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

è in realtà solo readInt. Stai selezionando un valore Maybe per mettere insieme lo stesso.

Per il resto, penso che il modo più succinto in questo caso sia utilizzare la funzione maybe. Poi si può ottenere solo

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

È necessario l'istanza alternativa per 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 

Vale a dire calcola il primo argomento e restituisci il valore se è Just, altrimenti calcola il secondo argomento e restituisci il valore.

Il codice:

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

Un altro modo per farlo è con l'iterative monad transformer dal pacchetto 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' 

Dove cutoff specifica il numero massimo di tentativi.

Il vantaggio di questo approccio è che è possibile facilmente interleave other repeated actions con f', grazie all'istanza MonadPlus di IterT. Ad esempio, registrare le azioni o attendere le azioni.

Problemi correlati