Haskell: Posso usare una clausola in cui, dopo un blocco con gli operatori bind (>> =)?
Domanda
Ho una domanda molto semplice. Mi piacerebbe usare una clausola where dopo un blocco di codice che utilizza gli operatori legano ma ottengo un errore di compilazione.
Ecco un semplice esempio:
main =
putStrLn "where clause test:" >>
return [1..10] >>= \list ->
print list'
where list' = reverse list -- test1.hs:5:28: Not in scope: `list'
posso usare una clausola let per la lista' come in
main =
putStrLn "where clause test:" >>
return [1..10] >>= \list ->
let list' = reverse list -- works of course
in print list'
ma mi piacerebbe davvero se potevo usare una clausola where ...
Ho provato anche con la notazione non
main = do
putStrLn "where clause test:"
list <- return [1..10]
print list'
where list' = reverse list --test3.hs:5:30: Not in scope: `list'
Lo stesso problema. Posso usare una clausola where in queste circostanze?
Soluzione
Come spiega ephemient, non è possibile utilizzare where
Clausole il modo di fare.
L'errore si verifica perché in questo codice:
main =
return [1..10] >>= \list ->
print list'
where
list' = reverse list
Il where
-clausola è attaccato alla funzione principale.
Ecco che la stessa funzione con più parentesi:
main = return [1..10] >>= (\list -> print list')
where
list' = reverse list
Credo che la sua abbastanza ovvio il motivo per cui si ottiene l'errore "out of scope
": l'associazione per list
è profondo l'espressione main
, non qualcosa la clausola where
può raggiungere
Quello che faccio di solito in questa situazione (e sono stato morso dalla stessa cosa un mucchio di volte). Ho semplicemente introdurre una funzione e passare il list
come argomento.
main = do
list <- return [1..10]
let list' = f list
print list'
where
f list = reverse list -- Consider renaming list,
-- or writing in point-free style
Naturalmente, immagino il codice vero e proprio nella funzione f
è molto più che appena reverse
ed è per questo che si desidera all'interno di una clausola di where
, invece di un legame let
linea. Se il codice all'interno della funzione f
è molto piccolo, avevo appena scrivo all'interno vincolante il let
, e non vorrei passare attraverso il sovraccarico di introdurre una nuova funzione.
Altri suggerimenti
Il problema è che let
-in
è un'espressione, che può essere utilizzato all'interno di altre espressioni, mentre where
può essere utilizzata solo su un (modulo | classe | esempio | GADT | ...) dichiarazione o un (funzione | reticolo ) vincolante.
Dal rapporto Haskell 98 sulle dichiarazioni rel="noreferrer"> ,
p | g 1
=
e 1
| g 2=
e 2
...
| g m=
e m
where {
decls}
è lo zucchero per
p
= let
declsin
if
g 1then
e 1else
if
g 2then
e 2else
...
if
g mthen
e melse error "Unmatched pattern"
o, semplificando le cose rimuovendo guardie,
p
=
ewhere {
decls}
è lo zucchero per
p
= let
declsin
e
in entrambe le legature funzione e modello. Questo è vero anche quando il e è un do {
... }
costrutto.
Se si desidera avere un locale, il legame con un particolare sottoespressione all'interno di un'espressione più grande, è necessario utilizzare let
-in
(o semplicemente let
all'interno di un do
, ma questo è solo zucchero per let
-in
).
Non si può nemmeno scrivere
main = do
putStrLn "where clause test: "
list <- return [1..10]
(print list' where list' = reverse list)
perché " e where {
decls }
" non è un'espressione giuridica -. where
può essere utilizzato solo nelle dichiarazioni e attacchi
main = do
putStrLn "where clause test: "
list <- return [1..10]
let list' = list'' where list'' = reverse list
print list'
Questa è legale (se un po 'artificiosa).
Per quanto posso dire, la clausola where viene utilizzato solo in attacchi locali . La parte interna di un >> (=) dichiarazione vincolante non è un locale vincolanti (due diversi tipi di attacchi in quella frase).
Confronto con questo:
main = f [1..10]
f list =
putStrLn "where clause test:" >> print list'
where list' = reverse list
Si potrebbe desiderare di fare riferimento alla Haskell 98 sintassi rapporto - non sicuro di quanto l'aiuto sarebbe stato.
Se mi sbaglio, qualcuno certamente mi corregga, ma sono abbastanza sicuro che non è possibile utilizzare una clausola in cui a tutti in stile che hai mostrato in precedenza. list
non sarà mai portata ad una clausola in cui a meno che non si tratta di un parametro alla funzione.