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

Синтаксис-выделенная версия: ioref.hs

Спасибо за вашу помощь!

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
Другие вопросы по тегам