2012-12-30 12 views
19

Sto tentando di ridefinire una chiamata di funzione mapM_ all'interno di un blocco do in Haskell. Vorrei estrarre il lambda in una funzione denominata (localmente) per rendere il codice più leggibile.Sintassi della clausola Haskell all'interno di un blocco do

mio codice è originariamente come questo:

do 
    -- ... 
    mapM_ (\x -> x + 1) aList 

    return aValue 

vorrei cambiarlo in

do 
    -- ... 
    mapM_ func aList 
    where func x = x + 1 

    return aValue 

ma sto ottenendo un errore di sintassi sulla linea return aValue. Il mio lambda attuale è più complicato :-), ma l'ho provato con lo stesso lambda per assicurarmi che non fosse un problema nel codice lambda.

Come posso riscrivere questo codice? Dovrei usare let ... in invece?

risposta

27

Ci sono tre simili (ma distinti) modi di definire roba qui:

  • È possibile allegare where clausole dopo alcune definizioni - per lo più attacchi equazione in stile . Quindi potresti metterne uno alla fine della tua funzione, o dopo qualcosa definito con let o una clausola circostante where.

  • D'altra parte, è un let x = ... in ...espressione che restituisce il pezzo dopo in, che è l'unico posto la roba dopo let è visibile.

  • All'interno di un blocco do, poiché esiste già un nidificazione implicita dell'ambito (le cose sono visibili dopo la prima definizione), è possibile utilizzare solo let x = .... Questa è la stessa cosa della forma precedente: il resto del blocco do dopo il let è effettivamente la parte in ....

Se si desidera una definizione locale che utilizza qualcosa di definito all'interno del blocco do, l'unica scelta è il terzo (o passando l'altro valore (s) come argomento (s)). Per un helper indipendente funziona come il tuo esempio, tuttavia, qualsiasi stile funziona.Ecco il tuo esempio, per dimostrare ogni:

Il primo stile, dove func è visibile in qualsiasi parte foo, compresa qualsiasi altra cosa definito nella clausola where:

foo = do ... 
     mapM_ func aList 
     ... 
     return aValue 
    where func x = x + 1 

Il secondo stile, dove func è visibile solo all'interno la let espressione, che in questo caso è l'intero blocco do:

foo = let func x = x + 1 
     in do 
     ... 
     mapM_ func aList 
     ... 
     return aValue 

il terzo stile, definendo all'interno del blocco do. In questo caso, func è visibile solo dopo lo let; nel primo ... non è stato ancora definito.

foo = do ... 
     let func x = x + 1 
     mapM_ func aList 
     ... 
     return aValue 

Oh, e per buona misura: Dal let ... in ... è un'espressione, si può anche usare ovunque ci sia un'espressione, solo per citarne alcune definizioni locali. Quindi, ecco un altro esempio:

foo = do ... 
     let func x = x + 1 in mapM_ func aList 
     ... 
     return aValue 

Come in precedenza, func è visibile solo all'interno del let espressione, che in questo caso è la sola espressione dopo che, in nessun altro luogo.

+0

Grazie. La terza forma sembra che mi consenta di definire la funzione lambda abbastanza vicina alla 'mapM_' per essere utile. Sono solo preoccupato che inquini il namespace della funzione di primo livello con il nome 'func' definito nel' let' (problema minore). – Ralph

+0

@Ralph: Beh, è ​​solo questione di quale ambito si vuole rendere visibile. Se il tuo blocco 'do' è abbastanza grande da doverti preoccupare di inquinare lo spazio dei nomi al suo interno, dovresti probabilmente considerare di dividerlo in parti più piccole in ogni caso . :] –

+0

Sì, lo era anche per me. Sto traducendo un codice Scala funzionale che qualche idiota ha scritto (:-)) con funzioni che sono più lunghe di quanto dovrebbero essere. – Ralph

3

Non dovrebbe essere il tuo where alla fine della funzione?

Ti piace questa:

function aList aValue = do 
    mapM_ func aList 
    return aValue 
    where func x = x + 1 
+0

non sono sicuro. Mi piacerebbe trovare un modo per estrarre il lambda in una funzione molto locale per migliorare la leggibilità. Se devo metterlo alla fine della funzione di primo livello, ciò lo rende molto meno utilizzabile. Sono un noob Haskell. – Ralph

10

Un'altra opzione è utilizzare forM_ anziché mapM_, che capovolge l'ordine degli argomenti. È quindi possibile utilizzare l'operatore $ con un'espressione lambda finale in questo modo:

do 
    forM_ aList $ \x -> do 
    ... 

    return aValue 
+0

Non avevo considerato quell'alternativa. Guarderò di nuovo il mio codice per vedere se è più leggibile. In altri casi in cui ho bisogno di un attraversamento di una lista monadica, ma non esiste una comoda versione capovolta come 'forM_', posso semplicemente usare' flip' per creare una nuova funzione. – Ralph

Problemi correlati