2016-06-26 16 views
7

Ich bin ziemlich verwirrt mit dem Konzept der unveränderlichen Variablen in Haskell. Es scheint, dass wir den Wert von Variablen in Haskell nicht ändern können. Aber als ich versuchte, in GHCI folgenden Code, schien es wie der Wert der Variablen geändert hat:Was bedeutet unveränderliche Variable 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 

Hat diesen Konflikt mit der Idee der unveränderlichen Variablen?

Vielen Dank!

+1

das funktioniert nicht in meinem ... – Netwave

+0

Versuchen Sie, 'a = 1; a = 2' innerhalb einer '.hs' Datei und kompiliere sie mit' ghc' ... – Bakuriu

+8

GHCi Befehle sind IO-Aktionen, also ist es so, als ob alles, was du tippst, in einem 'do'-Block liegt. Das bedeutet, dass die nachfolgende Zuweisung an denselben Namen wie verschachtelt ist. Lass uns die alte Bindung * shadow *. – Bakuriu

Antwort

18

Mit Haskell können Sie keine vorhandenen Variablen ändern. Es erlaubt Ihnen jedoch, Variablennamen wiederzuverwenden, und das ist alles, was hier passiert. Eine Möglichkeit, dies zu sehen ist GHCi zu stellen, mit der :i[nfo] Richtlinie, in dem die Variable deklariert wurde:

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 

Diese sind eigentlich zwei ganz getrennte, verschiedene Variablen, die nur der gleiche Name genannt werden passieren! Wenn Sie nur für a fragen, wird die neuere Definition sein “ ” bevorzugt, aber die alte ist immer noch da – eine Möglichkeit, dies zu sehen, wie Chi in den Kommentaren bemerkt, ist a in einer Funktion zu verwenden:

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 muss nie darauf achten, dass Sie eine neue Variable definiert haben, die auch a heißt; aus seiner Sicht a war eine unveränderliche Variable, die immer so bleibt wie sie ist.


Es lohnt sich, ein wenig darüber zu sprechen, warum GHCi die spätere Definition bevorzugt. Dies geschieht nicht sonst in Haskell Code passieren; wenn Sie insbesondere versuchen, das folgende Modul zu kompilieren, gibt es einfach einen Fehler in Bezug auf doppelte Definition:

a = 1 
a = 2 

main :: IO() 
main = print a 

Der Grund, dass so etwas in GHCi erlaubt ist, ist, dass es von Haskell-Modulen unterschiedliche arbeitet. Die Sequenz von GHCi-Befehlen bildet tatsächlich eine Sequenz von Aktionen in der IO-Monade& dagger;; dh das Programm würde

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

Jetzt sein, wenn Sie über Monaden gelernt haben, wissen Sie, dass dies für

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

nur syntaktischer Zucker ist und das ist wirklich der entscheidende Bit dafür, warum Sie können den Namen a wiederverwenden: der zweite, a = 2, lebt in einem engeren Bereich als der erste. Es ist also lokaler und lokale Definitionen haben Priorität.Ob das eine gute Idee ist, ist ein wenig umstritten; ein gutes Argument dafür ist, dass Sie eine Funktion wie

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

und es gerade arbeiten, werden nicht aufhören können, weil jemand anderer Stelle

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

definiert, es ist wirklich sehr nützlich, dass man “ neu definieren ” Variablen während herumalbern in GHCi, auch wenn es nicht ist so eine gute Idee, Sachen in einem richtigen Programm neu zu definieren, das konsistentes Verhalten zeigen soll.


& dolch; Wie Dfeuer bemerkt, ist dies nicht die ganze Wahrheit. Sie können einige Dinge in GHCi tun, die in einem IO Do-Block nicht erlaubt sind, insbesondere können Sie data Typen definieren und class es. Aber jede normale Aussage oder variable Definition wirkt sozusagen in der IO Monade.

+0

Eine Sache fehlt: GHCi erlaubt 'data',' newtype', 'type',' class' und 'instance' Deklarationen, die in der Haskell' do'-Syntax nicht erlaubt sind. – dfeuer

+1

Angesichts der Tatsache, dass GHCi eine * interaktive * Umgebung ist, ist es ein wesentliches Merkmal. Wie oft ist es passiert, dass eine Definition falsch geschrieben wurde? Wenn Sie jedes Mal einen komplett neuen Namen wählen, würden Sie leicht mit unlesbaren Namen wie 'myFunction7' enden, wo alle vorherigen Definitionen von' myFunction' einen Fehler enthalten, und Sie müssen daran denken, immer '7' hinzuzufügen, wenn mit der richtigen Definition ... – Bakuriu

+0

Richtig. Wenn dies erforderlich wäre, müssten wir einen völlig anderen Workflow anpassen, wahrscheinlich etwas mit expliziten Scopings. – leftaroundabout

0

(Die andere Antwort GHCi verwendet, ist in Ordnung, aber es ist nicht spezifisch für GHCi oder Monaden ... zu klären)

Wie Sie aus der Tatsache sehen, dass das folgende Haskell Programm

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

druckt 8 anstatt 10, Variablen können nur "gebunden", nicht "mutiert", in Haskell. Mit anderen Worten, über das Programm entspricht (so genannten α-äquivalent, nur eine konsequente Änderung des Namen von gebundenen Variablen Bedeutung; https://wiki.haskell.org/Alpha_conversion für weitere Details) zu

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

wo klar ist, dass x1 und x2 sind verschiedene Variablen.