-
Notifications
You must be signed in to change notification settings - Fork 0
/
Eval.hs
176 lines (136 loc) · 4.87 KB
/
Eval.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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
{-# OPTIONS_GHC -cpp -fglasgow-exts -fno-warn-orphans -fallow-overlapping-instances -funbox-strict-fields -fallow-undecidable-instances #-}
module Pugs.AST.Eval where
import Pugs.Internals
import Pugs.Cont hiding (shiftT, resetT)
import Control.Exception (try, Exception)
import Pugs.AST.SIO
import {-# SOURCE #-} Pugs.AST.Internals
{- Eval Monad -}
type Eval = EvalT (ContT Val (ReaderT Env SIO))
newtype EvalT m a = EvalT { runEvalT :: m a }
instance ((:>:) (Eval a)) (SIO a) where cast = liftSIO
runEvalSTM :: Env -> Eval Val -> STM Val
runEvalSTM env = runSTM . (`runReaderT` enterAtomicEnv env) . (`runContT` return) . runEvalT
runEvalIO :: Env -> Eval Val -> IO Val
runEvalIO env = runIO . (`runReaderT` env) . (`runContT` return) . runEvalT
tryIO :: a -> IO a -> Eval a
tryIO err = lift . liftIO . (`catchIO` (const $ return err))
{-|
'shiftT' is like @callCC@, except that when you activate the continuation
provided by 'shiftT', it will run to the end of the nearest enclosing 'resetT',
then jump back to just after the point at which you activated the continuation.
Note that because control eventually returns to the point after the
subcontinuation is activated, you can activate it multiple times in the
same block. This is unlike @callCC@'s continuations, which discard the current
execution path when activated.
See 'resetT' for an example of how these delimited subcontinuations actually
work.
-}
shiftT :: ((a -> Eval Val) -> Eval Val)
-- ^ Typically a lambda function of the form @\\esc -> do ...@, where
-- @esc@ is the current (sub)continuation
-> Eval a
shiftT e = EvalT . ContT $ \k ->
runContT (runEvalT . e $ lift . lift . k) return
{-|
Create an scope that 'shiftT'\'s subcontinuations are guaranteed to eventually
exit out the end of.
Consider this example:
> resetT $ do
> alfa
> bravo
> x <- shiftT $ \esc -> do
> charlie
> esc 1
> delta
> esc 2
> return 0
> zulu x
This will:
1) Perform @alfa@
2) Perform @bravo@
3) Perform @charlie@
4) Bind @x@ to 1, and thus perform @zulu 1@
5) Fall off the end of 'resetT', and jump back to just after @esc 1@
6) Perform @delta@
7) Bind @x@ to 2, and thus perform @zulu 2@
8) Fall off the end of 'resetT', and jump back to just after @esc 2@
6) Escape from the 'resetT', causing it to yield 0
Thus, unlike @callCC@'s continuations, these subcontinuations will eventually
return to the point after they are activated, after falling off the end of the
nearest 'resetT'.
-}
resetT :: Eval Val -- ^ An evaluation, possibly containing a 'shiftT'
-> Eval Val
resetT e = lift . lift $
runContT (runEvalT e) return
instance Monad Eval where
return a = EvalT $ return a
m >>= k = EvalT $ do
a <- runEvalT m
runEvalT (k a)
fail str = do
pos <- asks envPos'
shiftT . const . return $ errStrPos (cast str) pos
instance MonadTrans EvalT where
lift x = EvalT x
instance Functor Eval where
fmap f (EvalT a) = EvalT (fmap f a)
instance MonadIO Eval where
liftIO io = EvalT (liftIO io)
instance MonadError Val Eval where
throwError err = do
pos <- asks envPos'
shiftT . const . return $ errValPos err pos
catchError _ _ = fail "catchError unimplemented"
{-|
Perform an IO action and raise an exception if it fails.
-}
guardIO :: IO a -> Eval a
guardIO io = do
rv <- liftIO $ try io
case rv of
Left e -> fail (show e)
Right v -> return v
{-|
Like @guardIO@, perform an IO action and raise an exception if it fails.
If t
supress the exception and return an associated value instead.
-}
guardIOexcept :: MonadIO m => [((Exception -> Bool), a)] -> IO a -> m a
guardIOexcept safetyNet io = do
rv <- liftIO $ try io
case rv of
Right v -> return v
Left e -> catcher e safetyNet
where
catcher e [] = fail (show e)
catcher e ((f, res):safetyNets)
| f e = return res
| otherwise = catcher e safetyNets
guardSTM :: STM a -> Eval a
guardSTM stm = do
rv <- liftSTM $ fmap Right stm `catchSTM` (return . Left)
case rv of
Left e -> fail (show e)
Right v -> return v
instance MonadSTM Eval where
liftSIO = EvalT . lift . lift
liftSTM stm = do
atom <- asks envAtomic
if atom
then EvalT (lift . lift . liftSTM $ stm)
else EvalT (lift . lift . liftIO . liftSTM $ stm)
instance MonadReader Env Eval where
ask = lift ask
local f m = EvalT $ local f (runEvalT m)
instance MonadCont Eval where
-- callCC :: ((a -> Eval b) -> Eval a) -> Eval a
callCC f = EvalT . callCCT $ \c -> runEvalT . f $ \a -> EvalT $ c a
{-
instance MonadEval Eval
class (MonadReader Env m, MonadCont m, MonadIO m, MonadSTM m) => MonadEval m
-- askGlobal :: m Pad
-}
retError :: (Show a) => String -> a -> Eval b
retError str a = fail $ str ++ ": " ++ show a