2017-08-21 1 views
5

Hintergrund

Ich bin ein Schemer lernen Haskell zu lernen. Ich versuche, einen Scheme-Interpreter in C zu implementieren, der chapter 4 von SICP folgt. Es stellt sich heraus, dass die Programmierung direkt in C zu hart ist. Also entscheide ich mich für einen Prototyp in Haskell. Mit Hilfe von Write Yourself a Scheme in 48 Hours habe ich alles außer Variablen, Schließungen und Umgebung implementiert.IORef bezieht sich immer noch auf den alten Wert nach dem Update

Problem

Die Modifikation IORef persistieren nicht zwischen Invokationen von main. Ich erwarte, dass das Programm druckt (Falsch) (True) (True) (True) ... aber tatsächlich druckt es (Falsch) (True) (Falsch) (True) (Falsch) (True) .. .

Streifen-down Version des Codes:

import Data.IORef 

data SCM = Environment (IORef Bool) SCM | Empty'Environment 

global :: IO SCM 
global = Environment <$> newIORef False <*> pure Empty'Environment 

print'' :: SCM -> IO() 
print'' ls = 
    case ls of 
    Empty'Environment -> pure() 
    Environment first rest -> readIORef first >>= putStr . show >> print'' rest 

print' :: SCM -> IO() 
print' ls = putStr "(" *> print'' ls *> putStrLn ")" 

main :: IO() 
main = global >>= 
     \ls -> case ls of 
       Empty'Environment -> pure() 
       Environment first _ -> print' ls *> 
             modifyIORef first (const True) *> 
             print' ls *> 
             main 

Syntax-markierte Version: ioref.hs

Vielen Dank für Ihre Hilfe!

+3

'global' schaffen neue' SCM' mit neuen 'IORef'. Sie sollten Ihre Schleife reorganisieren, wobei 'global' einmal ausgeführt wird. – freestyle

Antwort

7

Wir können Ihr Beispiel auf main = (global >>= loop) >> main schneiden. Das Problem ist, dass global keine einzige globale Variable ist, sondern eine IO SCM, eine Aktion, die den globalen Wert erstellt. Lassen Sie uns es umbenennen:

createSCM :: IO SCM 
createSCM = Environment <$> newIORef False <*> pure Empty'Environment 

Jetzt ist der Name näher an der Wahrheit. Wir erstellen ein neues SCM jedes Mal, wenn wir diese Funktion aufrufen. So Ihr Programm funktioniert wie folgt:

main = (createSCM >>= loop) >> main 
    = (createSCM >>= loop) >> (createSCM >>= loop) >> main 
    = (createSCM >>= loop) >> (createSCM >>= loop) >> ... 

Wie Sie sehen können wir neue SCM die ganze Zeit erstellen. Daher erhalten Sie nicht das erwartete Verhalten.

Die Lösung ist einfach. Erstellen Sie Ihre global und loop ausdrücklich:

main = do 
    global <- createSCM 
    let loop = do 
     ... 
     loop 
    loop 
+0

Vielen Dank, also war mein Missverständnis, es als global zu betrachten. Aber tatsächlich verhält es sich eher wie ein Konstrukteur, oder? –

+0

@AlexVong rechts. Sie können sich ein 'IO a' als _action_ vorstellen, das bei Ausführung ein' a' ergibt. Zum Beispiel gibt 'readLine :: IO String' Ihnen eine 'String', wenn Sie sie ausführen, aber Sie erwarten nicht, dass' readLine' für jeden Aufruf dieselbe Zeichenfolge zurückgibt (dies hängt von der Benutzereingabe ab). – Zeta

Verwandte Themen