IORef все еще ссылается на старое значение после обновления
Фон
Я - Schemer, начинающий изучать Haskell. Я пытаюсь реализовать интерпретатор Scheme в C, следуя главе 4 SICP. Оказывается, программирование непосредственно на C слишком сложно. Поэтому я решил сделать первый прототип на Хаскеле. С помощью " Напиши себе схему" за 48 часов я реализовал все, кроме переменных, замыканий и окружения.
проблема
Модификация для IORef
не сохраняется между вызовами main
, Я ожидаю, что программа напечатает (False) (True) (True) (True)... но на самом деле она печатает (False) (True) (False) (True) (False) (True)...
Сокращенная версия кода:
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
Спасибо за вашу помощь!
1 ответ
Мы можем сократить ваш пример до main = (global >>= loop) >> main
, Проблема в том, что global
не является одной глобальной переменной, а IO SCM
, действие, которое создаст глобальное значение. Давайте переименуем это:
createSCM :: IO SCM
createSCM = Environment <$> newIORef False <*> pure Empty'Environment
Теперь имя ближе к истине. Мы создаем новый SCM
каждый раз, когда мы называем эту функцию. Итак, ваша программа работает так:
main = (createSCM >>= loop) >> main
= (createSCM >>= loop) >> (createSCM >>= loop) >> main
= (createSCM >>= loop) >> (createSCM >>= loop) >> ...
Как видите, мы создаем новые SCM
все время. Поэтому вы не получите ожидаемого поведения.
Решение простое. Создайте свой global
а также loop
в явном виде:
main = do
global <- createSCM
let loop = do
...
loop
loop