2014-11-23 24 views
6

So che la domanda è stata posta there ma non posso credere che non ci sia una risposta diretta.Cortocircuito AND dentro IO Monad

ho capito che non è buono per nascondere effetto collaterale all'interno di una (& &), ma nel mio caso l'effetto collaterale sono solo controllando qualcosa nel mondo outstide (esistenza di un file, controllare il tempo di modifica ecc, chiedere all'utente un sì/no quistione).

Quindi, qual è il modo haskell per qualcosa di simile, in modo che cond2 non venga eseguito se cond1 è falso.

cond1, cond2 :: IO bool 


main = do 
    cond <- liftM2 (&&) con1 con2 
    if cond 
    then result1 
    else result2 

mi aspettavo qualcosa di simile cond <- all [con1, con2] o equivalente, ma non riesco a trovare nulla.

Aggiornamento

Posso vedere un sacco di soluzione manuale. Sono ancora confuso dal fatto che questa funzione non esista da qualche parte. Un vantaggio della valutazione di lazzy è che non solo cortocircuito per codifica codificata && come in C. È davvero strano che quando in imperativo nella modalità, Haskell non riesca nemmeno a cortocircuitare &&. Sebbene, tutte le soluzioni utilizzino in qualche modo e se cortocircuitare la valutazione. Non c'è un modo per creare un generico lazzy liftM2?

+0

monad-loops senza l'utilizzo di combinatori speciali: 'andm [] = TRUE tornare e poi' andm (x: XS) = do b <- x; se b allora eM xs else restituiscono False' – ErikR

+0

Come ho detto di seguito, 'andM' è definito nella libreria monad-loops, dove ci si aspetta di trovare cose come questa http://hackage.haskell.org/package/monad-loops -0.4.2.1/docs/Control-Monad-Loops.html # v: andM – Michael

+0

@Arthur non ha visto l'aggiornamento. – mb14

risposta

4

Questo non è diverso da quello che alcuni altri hanno da dire, ma non è vero più semplice solo per emulare la definizione di and:

andM = foldr (&&&) (return True) 
    where ma &&& mb = ma >>= \p -> if p then mb else return p 

allora otteniamo, dire:

> let test1 = putStrLn "This test succeeds" >> return True 
> let test2 = putStrLn "This test fails" >> return False 
> andM [test1,test2,undefined,undefined] 
This test succeeds 
This test fails 
False 

Se andM non ha "cortocircuito", la cella non definita sarebbe stata valutata e restituita un'eccezione.

È un po 'irritante che liftM2 (&&) non funzioni come si spera.

Edit: Ho appena notato che, come ci si sarebbe aspettati, questo è definito nel pacchetto http://hackage.haskell.org/package/monad-loops-0.4.2.1/docs/src/Control-Monad-Loops.html#andM

3

L'operazione desiderata è semplice da definire esplicitamente.

shortCircuitAnd :: Monad m => m Bool -> m Bool -> m Bool 
shortCircuitAnd x y = do 
    r1 <- x -- perform effect of x 
    if r1 
    then y -- perform effect of y and return result 
    else return False -- do *not* evaluate or perform effect of y 

Naturalmente, è possibile utilizzare questa funzione infisso così, usando apici inversi:

x `shortCircuitAnd` y == shortCircuitAnd x y 
+1

'r1 && r2' è in realtà' r2' poiché 'r1' è garantito da' if'. Quindi, 'do r1 <- x; se r1 allora qualcun altro restituisce False' funziona. – chi

8

Questo è ciò che Pipes.Prelude.and fa, andando su un flusso pigro dei condizionali effectfully-generate e corto circuito se nessuno di loro sono False:

import Pipes (each) 
import qualified Pipes.Prelude as Pipes 

conds :: [IO Bool] 
conds = ... 

main = do 
    cond <- Pipes.and (each conds >-> Pipes.sequence) 
    print cond 

link pertinenti:

3

possiamo utilizzare un trasformatore monade come MaybeT, che è un esempio di MonadPlus. L'idea è di utilizzare guard per trasformare i risultati di False in mzero s che interromperà il calcolo. Quindi convertiamo lo Maybe risultante in Bool.

import Control.Monad.Trans 
import Control.Monad.Trans.Maybe 

effyand :: (Functor m, Monad m) => [m Bool] -> m Bool 
effyand = fmap isJust . runMaybeT . mapM_ (lift >=> guard) 
+1

Penso che sia meglio aggiungere una piccola spiegazione alla tua risposta piuttosto che lasciare un blocco di codice. – AdamMc331

3

avrei definisco un monoide come

newtype AllM m = AllM { getAllM :: m Bool } 

instance (Monad m) => Monoid (AllM m) where 
    mempty = AllM (return True) 
    mappend (AllM u) (AllM v) = AllM $ 
     u >>= \x -> if x then v else return False 

e poi getAllM . mconcat . map AllM $ [con1, con2].