5

Background

I am a Schemer starting to learn Haskell. I am trying to implement a Scheme interpreter in C, following chapter 4 of SICP. It turns out programming directly in C is too hard. So I decide to first prototype in Haskell. With the help of Write Yourself a Scheme in 48 Hours, I have implemented everything except variables, closures and environment.

Problem

The modification to IORef doesn't persist between invocations of main. I expect the program to print (False) (True) (True) (True)... but in fact it prints (False) (True) (False) (True) (False) (True)...

Strip-down version of the code:

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-highlighted version: ioref.hs

Thanks for your help!

4

1 回答 1

7

我们可以将您的示例缩减为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的。因此,您不会得到预期的行为。

解决方案很简单。创建你的globalloop明确的:

main = do
  global <- createSCM
  let loop = do
         ...
         loop
  loop
于 2017-08-21T11:36:30.457 回答