-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathMonadic.hs
170 lines (135 loc) · 3.76 KB
/
Monadic.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
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ConstraintKinds #-}
{-# OPTIONS_GHC
-fplugin BinderAnn.Monadic
-fplugin-opt BinderAnn.Monadic:manual
#-}
module Monadic where
import Prelude hiding (lookup)
import qualified Data.List as List
import Control.Monad.State
import Control.Monad.Except
import BinderAnn.Monadic
----------------------------------------
-- Example 1: Statefull arithmetic evaluator
----------------------------------------
-- the state layer
newtype Var = Var Int deriving (Show, Eq)
type Env = [(Var, Double)]
type CallStack = [SrcInfo]
data EvalState =
EvalState
{ st_uniq :: Var
, st_env :: Env
, st_stack :: CallStack
} deriving Show
emptyState :: EvalState
emptyState = EvalState (Var 0) [] []
-- the error handling layer
data EvalError =
VarNotFound CallStack Var
| DivByZero CallStack
deriving Show
----------------------------------------
-- the evaluation monad
type MonadEval m =
( MonadState EvalState m
, MonadError EvalError m
)
type Eval = StateT EvalState (ExceptT EvalError IO)
runEval :: Eval Double -> IO (Either EvalError Double)
runEval = runExceptT . flip evalStateT emptyState
getEnv :: MonadEval m => m Env
getEnv = gets st_env
getCallStack :: MonadEval m => m CallStack
getCallStack = gets st_stack
lookup :: MonadEval m => Var -> m Double
lookup i = do
e <- getEnv
case List.lookup i e of
Just n -> return n
Nothing -> do
stack <- getCallStack
throwError (VarNotFound stack i)
store :: MonadEval m => Double -> m Var
store n = state $ \(EvalState (Var u) e p) ->
(Var u, EvalState (Var (u + 1)) ((Var u, n) : e) p)
binop :: MonadEval m => (Double -> Double -> Double) -> m Var -> m Var -> m Var
binop f ex ey = do
x <- lookup =<< ex
y <- lookup =<< ey
store (f x y)
----------------------------------------
-- annotation support
instance AnnotatedM Eval a where
annotateM m info =
-- Note; using a do statement here will produce an infinite annotation loop!
-- In practice this shouldn't happen, as this instance won't appear in a
-- module using the plugin.
modify (\st -> st { st_stack = info : st_stack st }) >> m
----------------------------------------
-- the DSL
var :: MonadEval m => Var -> m Var
var = return
lit :: MonadEval m => Double -> m Var
lit = store
ret :: MonadEval m => m Var -> m Double
ret ex = lookup =<< ex
(.+) :: MonadEval m => m Var -> m Var -> m Var
(.+) = binop (+)
(.-) :: MonadEval m => m Var -> m Var -> m Var
(.-) = binop subtract
(.*) :: MonadEval m => m Var -> m Var -> m Var
(.*) = binop (*)
(./) :: MonadEval m => m Var -> m Var -> m Var
(./) ex ey = do
y <- ey
vy <- lookup y
if vy /= 0
then binop (/) ex ey
else do
stack <- getCallStack
throwError (DivByZero stack)
----------------------------------------
-- tests
{-# ANN test1 SrcInfo #-}
test1 :: Eval Double
test1 = do
x <- lit 0
y <- lit 3 .+ var x
ret (var y ./ var x)
{-# ANN test2 SrcInfo #-}
test2 :: Eval Double
test2 = do
x <- lit 3 .+ var (Var 123)
ret (var x)
-- a test for Maciej's use case
newtype Foo a = Foo a
litFoo :: Double -> Eval (Foo Var)
litFoo x = Foo <$> lit x
{-# ANN test3 SrcInfo #-}
test3 :: Eval Double
test3 = do
Foo x <- litFoo 42
ret (var x ./ lit 0)
----------------------------------------
-- entry point
----------------------------------------
tests :: IO ()
tests = do
putStrLn "test1:"
runEval test1 >>= print
putStrLn "test2:"
runEval test2 >>= print
putStrLn "test3:"
runEval test3 >>= print
return ()