2016-06-26 68 views
7

Sono abbastanza confuso con il concetto di variabili immutabili in Haskell. Sembra che non possiamo cambiare il valore delle variabili in Haskell. Ma quando ho provato seguente codice nel GHCI, sembrava che il valore delle variabili è cambiata:Cosa significa variabile immutabile in Haskell?

Prelude> foo x=x+1 
Prelude> a=1 
Prelude> a 
1 
Prelude> foo a 
2 
Prelude> a=2 
Prelude> a 
2 
Prelude> foo a 
3 

Questo conflitto con l'idea di variabili immutabili?

Grazie mille!

+1

che non funziona nel mio modo ... – Netwave

+0

Prova a mettere 'a = 1; a = 2' all'interno di un file '.hs' e compilarlo con' ghc' ... – Bakuriu

+8

I comandi GHCi sono azioni IO, quindi è come se tutto ciò che si digita fosse all'interno di un blocco 'do'. Il che significa che il successivo incarico con lo stesso nome è come nidificato 'let's, tu * shadow * il vecchio bind. – Bakuriu

risposta

18

Haskell non consente di modificare variabili esistenti. Tuttavia, ti consente di riutilizzare i nomi delle variabili, , e questo è tutto ciò che sta accadendo qui. Un modo di vedere questo è quello di chiedere GHCi, usando la direttiva :i[nfo], in cui è stata dichiarata la variabile:

Prelude> let a = 1 
Prelude> :i a 
a :: Num a => a  -- Defined at <interactive>:2:5 
Prelude> let a = 2 
Prelude> :i a 
a :: Num a => a  -- Defined at <interactive>:4:5 

Questi sono in realtà due intero separato, diverse variabili, che ha appena capita di essere chiamato con lo stesso nome! Se basta chiedere per a, la definizione più recente sarà “ preferito ”, ma quello vecchio è ancora lì – un modo di vedere questo, come notato da chi nei commenti, è quello di utilizzare a in una funzione:

Prelude> let a = 2 
Prelude> :i a 
a :: Num a => a  -- Defined at <interactive>:4:5 
Prelude> let f x = a + x 
Prelude> let a = 3 
Prelude> f (-2) 
0 

f non deve mai preoccuparsi di aver definito una nuova variabile chiamata anche a; dal suo punto di vista a era una variabile immutabile che rimane sempre così com'è.


Vale la pena parlare del motivo per cui GHCi preferisce la definizione successiva. Questo è non altrimenti si verifica nel codice Haskell; in particolare se si tenta di compilare il seguente modulo, dà semplicemente un errore riguardante definizione duplicata:

a = 1 
a = 2 

main :: IO() 
main = print a 

La ragione per cui qualcosa di simile è consentito in GHCi è che funziona diverso dai moduli Haskell. La sequenza di comandi GHCi forma infatti una sequenza di azioni nella monade IO& dagger;; vale a dire il programma avrebbe dovuto essere

main :: IO() 
main = do 
    let a = 1 
    let a = 2 
    print a 

Ora, se hai imparato a conoscere monadi saprete che questo è lo zucchero sintattico per

main = 
    let a = 1 in (let a = 2 in (print a)) 

e questo è davvero il bit cruciale per il motivo è possibile riutilizzare il nome a: il secondo, a = 2, vive in un ambito più stretto rispetto al primo. Quindi è più locale e le definizioni locali hanno la priorità.Se questa è una buona idea è un po 'discutibile; un buon argomento per esso è che si può avere una funzione come

greet :: String -> IO() 
greet name = putStrLn $ "Hello, "++name++"!" 

e non sarà smettere di lavorare solo perché qualcuno definisce altrove

name :: Car -> String 
name car | rollsOverAtRightTurn car = "Reliant Robin" 
     | fuelConsumption car > 50*litrePer100km 
             = "Hummer" 
     | ...      = ... 

Inoltre, è davvero molto utile che si può “ redefine ” variabili mentre si scherza in GHCi, anche se è non una così buona idea per ridefinire le cose in un programma corretto, che dovrebbe mostrare un comportamento coerente.


e pugnale; Come osserva Dfeuer, questa non è tutta la verità. È possibile eseguire alcune operazioni in GHCi che non sono consentite in un blocco di IO, in particolare è possibile definire i tipi data e class es. Ma qualsiasi affermazione normale o definizione di variabile si comporta come nella monade IO.

+0

Manca una cosa: GHCi consente le dichiarazioni 'data',' newtype', 'type',' class' e 'instance', che non sono consentite nella sintassi' do' di Haskell. – dfeuer

+1

Dato che GHCi è un ambiente * interattivo * è una caratteristica essenziale. quante volte è capitato di mistificare una definizione? Se ogni volta che lo fai devi scegliere un nome completamente nuovo, ti ritroveremmo facilmente con nomi illeggibili come 'myFunction7' dove tutte le definizioni precedenti di' myFunction' contengono un errore, e devi ricordarti di aggiungere sempre '7' quando usando la definizione corretta ... – Bakuriu

+0

Giusto. Se ciò fosse necessario, avremmo bisogno di adattare un flusso di lavoro completamente diverso, probabilmente con un ambito esplicito. – leftaroundabout

0

(L'altra risposta utilizzando GHCi va bene, ma per chiarire che non è specifico per GHCi o monadi ...)

Come si può vedere dal fatto che il seguente programma Haskell

main = 
    let x = 1 in 
    let f y = x + y in 
    let x = 2 in 
    print (x * f 3) 

stampe 8 anziché 10, le variabili possono essere "vincolate", non "mutate", in Haskell. In altre parole, il programma di cui sopra è equivalente (chiamata α-equivalente, il che significa un cambiamento consistente dei nomi delle variabili vincolate solo, vedere https://wiki.haskell.org/Alpha_conversion per maggiori dettagli) per

main = 
    let x1 = 1 in 
    let f y = x1 + y in 
    let x2 = 2 in 
    print (x2 + f 3) 

in cui è chiaro che x1 e x2 sono variabili diverse.