-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathEnvironment.hs
66 lines (56 loc) · 1.65 KB
/
Environment.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
module Environment (
Environment,
nullEnv,
setVar,
getVar,
defineVar,
bindVars,
) where
import IError
import Control.Monad
import Control.Monad.Error
import Data.IORef
import System.IO
type Environment a = IORef [(String, IORef a)]
nullEnv :: IO (Environment a)
nullEnv = newIORef []
-- Check if a variable has been bound
isBound :: (Environment a) -> String -> IO Bool
isBound envRef var = readIORef envRef >>=
return . maybe False (const True) . lookup var
-- Get the value of a variable
getVar :: (Environment a) -> String -> IOThrowsError a a
getVar envRef var = do
env <- liftIO $ readIORef envRef
maybe
(throwError $ UnboundVar "Getting an unbound variable" var)
(liftIO . readIORef)
(lookup var env)
-- Set the value of a variable
setVar :: (Environment a) -> String -> a -> IOThrowsError a a
setVar envRef var val = do
env <- liftIO $ readIORef envRef
maybe
(throwError $ UnboundVar "Setting an unbound variable" var)
(liftIO . (flip writeIORef val))
(lookup var env)
return val
-- Define a new variable
defineVar :: (Environment a) -> String -> a -> IOThrowsError a a
defineVar envRef var val = do
alreadyDefined <- liftIO $ isBound envRef var
if alreadyDefined
then setVar envRef var val >> return val
else liftIO $ do
valRef <- newIORef val
env <- readIORef envRef
writeIORef envRef ((var, valRef) : env)
return val
-- Bind multiple variables
bindVars :: (Environment a) -> [(String, a)] -> IO (Environment a)
bindVars envRef vars = readIORef envRef >>= extendEnv vars >>= newIORef
where
extendEnv vars env = liftM (++ env) (mapM addBinding vars)
addBinding (var, val) = do
ref <- newIORef val
return (var, ref)