DynEditorExample.icl 11.6 KB
Newer Older
1 2
module DynEditorExample

Steffen Michels's avatar
Steffen Michels committed
3
import StdEnv
Tim Steenvoorden's avatar
Tim Steenvoorden committed
4 5
import Data.Func
import Data.Functor
6
import Text
Tim Steenvoorden's avatar
Tim Steenvoorden committed
7 8
import iTasks
import iTasks.Extensions.Editors.DynamicEditor
9

Tim Steenvoorden's avatar
Tim Steenvoorden committed
10 11 12

// Synonyms ////////////////////////////////////////////////////////////////////

Tim Steenvoorden's avatar
Tim Steenvoorden committed
13
:: List a :== [a]
Tim Steenvoorden's avatar
Tim Steenvoorden committed
14

Tim Steenvoorden's avatar
Tim Steenvoorden committed
15 16 17

// Main ////////////////////////////////////////////////////////////////////////

18 19
Start world = doTasks editTask world

Tim Steenvoorden's avatar
Tim Steenvoorden committed
20 21
editTask =
  forever
Tim Steenvoorden's avatar
Tim Steenvoorden committed
22
    ( enterInformation ("Contruct a task", info1) [EnterUsing id $ dynamicEditor taskEditor]
Tim Steenvoorden's avatar
Tim Steenvoorden committed
23
      >>= \v ->
Tim Steenvoorden's avatar
Tim Steenvoorden committed
24
          viewInformation ("Evaluate the task", info2) [] ()
Tim Steenvoorden's avatar
Tim Steenvoorden committed
25
            ||- (evalTaskConstExpr (toValue taskEditor v) <<@ ApplyLayout frameCompact)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
26
            >>= viewInformation ("Done!", info3) []
Tim Steenvoorden's avatar
Tim Steenvoorden committed
27
            >>= return
Tim Steenvoorden's avatar
Tim Steenvoorden committed
28 29
    )
where
Tim Steenvoorden's avatar
Tim Steenvoorden committed
30 31 32
  info1 = "Select the editors and combinators you'd like to use. When you're ready, push the 'Continue' button below to run your program."
  info2 = "Now step through the task you just created to test it."
  info3 = "The program is done, the result is given below."
33 34


Tim Steenvoorden's avatar
Tim Steenvoorden committed
35 36
// Data ////////////////////////////////////////////////////////////////////////

Tim Steenvoorden's avatar
Tim Steenvoorden committed
37
:: TaskExpr
Tim Steenvoorden's avatar
Tim Steenvoorden committed
38
  = Apply TaskFuncExpr Expr
Tim Steenvoorden's avatar
Tim Steenvoorden committed
39
  | EnterInfo String Ty
Tim Steenvoorden's avatar
Tim Steenvoorden committed
40 41 42 43
  | Then TaskExpr TaskFuncExpr
  | Or TaskExpr TaskExpr
  | And TaskExpr TaskExpr
  | When TaskExpr (List TaskContExpr)
44

Tim Steenvoorden's avatar
Tim Steenvoorden committed
45
:: TaskFuncExpr
Tim Steenvoorden's avatar
Tim Steenvoorden committed
46 47
  = ViewInfo String
  | UpdateInfo String
48

Tim Steenvoorden's avatar
Tim Steenvoorden committed
49 50 51
:: TaskContExpr
  = { name :: String, pred :: FuncExpr, cont :: TaskFuncExpr}

Tim Steenvoorden's avatar
Tim Steenvoorden committed
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
:: Expr
  = Int Int
  | Bool Bool
  | String String
  | Tuple Expr Expr
  | Fst Expr
  | Snd Expr
  | Eq Expr Expr

:: Value
  = VInt Int
  | VBool Bool
  | VString String
  | VTuple Value Value

Tim Steenvoorden's avatar
Tim Steenvoorden committed
67
:: FuncExpr
Tim Steenvoorden's avatar
Tim Steenvoorden committed
68 69 70 71 72 73 74 75 76
  = EqV Value
  | GrtV Value
  | LessV Value

:: Ty
  = E.a: Ty (a -> Value) & iTask a

:: Typed a b
  =: Typed a
77

Tim Steenvoorden's avatar
Tim Steenvoorden committed
78
derive class iTask TaskExpr, TaskFuncExpr, TaskContExpr, Expr, FuncExpr, Value, Typed
79

Tim Steenvoorden's avatar
Tim Steenvoorden committed
80 81
// These instances cannot be auto derived because of the existential quantifier.
// However, they will be never used, so we make them undefined.
Tim Steenvoorden's avatar
Tim Steenvoorden committed
82 83 84 85 86 87 88
gDefault{|Ty|} = undef
gEq{|Ty|} _ _ = undef
JSONEncode{|Ty|} _ _ = undef
JSONDecode{|Ty|} _ _ = undef
gText{|Ty|} _ _ = undef
gEditor{|Ty|} = undef

89

Tim Steenvoorden's avatar
Tim Steenvoorden committed
90 91
// Editor //////////////////////////////////////////////////////////////////////

Tim Steenvoorden's avatar
Tim Steenvoorden committed
92
taskEditor :: DynamicEditor TaskExpr
93 94
taskEditor = DynamicEditor conses
where
Tim Steenvoorden's avatar
Tim Steenvoorden committed
95
  conses =
Tim Steenvoorden's avatar
Tim Steenvoorden committed
96
    [ // This cons is used to provide untyped `TaskExpr` values.
Tim Steenvoorden's avatar
Tim Steenvoorden committed
97
      DynamicCons
Tim Steenvoorden's avatar
Tim Steenvoorden committed
98
        $ functionConsDyn "TaskExpr" "(enter task)" (dynamic \(Typed taskExpr) -> taskExpr ::  A.a: (Typed TaskExpr a) -> TaskExpr)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
99
        <<@@@ HideIfOnlyChoice
Tim Steenvoorden's avatar
Tim Steenvoorden committed
100
    , DynamicConsGroup "Combinators"
Tim Steenvoorden's avatar
Tim Steenvoorden committed
101
        [ functionConsDyn "Then" "sequence"
Tim Steenvoorden's avatar
Tim Steenvoorden committed
102
            ( dynamic \(Typed task) (Typed taskFunc) ->
Tim Steenvoorden's avatar
Tim Steenvoorden committed
103
              Typed (Then task taskFunc) ::
Tim Steenvoorden's avatar
Tim Steenvoorden committed
104
                A.a b:
Tim Steenvoorden's avatar
Tim Steenvoorden committed
105 106
                (Typed TaskExpr (Task a)) (Typed TaskFuncExpr (a -> Task b))
                -> Typed TaskExpr (Task b)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
107
            )
Tim Steenvoorden's avatar
Tim Steenvoorden committed
108
        , functionConsDyn "When" "guarded sequence"
Tim Steenvoorden's avatar
Tim Steenvoorden committed
109
            ( dynamic \(Typed task1) (Typed steps) ->
110 111
              Typed (When task1 steps) ::
              // Typed (When task1 [(expr, pred, tfExpr) \\ (Typed expr, pred, Typed tfExpr) <- steps]) ::
Tim Steenvoorden's avatar
Tim Steenvoorden committed
112
                A.a b:
Tim Steenvoorden's avatar
Tim Steenvoorden committed
113 114 115 116
                (Typed TaskExpr (Task a))
                (Typed (List TaskContExpr) (a -> Task b))
                // (Typed (List (Typed FuncExpr (a -> Bool), String, Typed TaskFuncExpr (a -> Task a))) (a -> Task b))
                -> Typed TaskExpr (Task b)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
117
            )
Tim Steenvoorden's avatar
Tim Steenvoorden committed
118 119
            <<@@@ applyHorizontalClasses
        , functionConsDyn "Or" "or"
Tim Steenvoorden's avatar
Tim Steenvoorden committed
120 121 122
            ( dynamic \(Typed task1) (Typed task2) ->
              Typed (Or task1 task2) ::
                A.a b:
Tim Steenvoorden's avatar
Tim Steenvoorden committed
123 124
                (Typed TaskExpr (Task a)) (Typed TaskExpr (Task a))
                -> Typed TaskExpr (Task a)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
125
            )
Tim Steenvoorden's avatar
Tim Steenvoorden committed
126
        , functionConsDyn "And" "and"
Tim Steenvoorden's avatar
Tim Steenvoorden committed
127 128 129
            ( dynamic \(Typed task1) (Typed task2) ->
              Typed (And task1 task2) ::
                A.a b:
Tim Steenvoorden's avatar
Tim Steenvoorden committed
130 131
                (Typed TaskExpr (Task a)) (Typed TaskExpr (Task b))
                -> Typed TaskExpr (Task (a, b))
Tim Steenvoorden's avatar
Tim Steenvoorden committed
132
            )
Tim Steenvoorden's avatar
Tim Steenvoorden committed
133
        , listConsDyn "List TaskContExpr" "continuations"
Tim Steenvoorden's avatar
Tim Steenvoorden committed
134 135 136
            ( dynamic \typedSteps ->
              Typed ((\(Typed expr) -> expr) <$> typedSteps) ::
                A.a b:
Tim Steenvoorden's avatar
Tim Steenvoorden committed
137
                (List (Typed TaskContExpr
Tim Steenvoorden's avatar
Tim Steenvoorden committed
138
                (a -> Task b)))
Tim Steenvoorden's avatar
Tim Steenvoorden committed
139
                -> Typed (List TaskContExpr) (a -> Task b)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
140 141
            )
            <<@@@ HideIfOnlyChoice
Tim Steenvoorden's avatar
Tim Steenvoorden committed
142 143 144
        , functionConsDyn "TaskContExpr" "continuation"
            ( dynamic \s (Typed func) (Typed taskFunc) ->
              Typed {name = s, pred = func, cont = taskFunc} ::
Tim Steenvoorden's avatar
Tim Steenvoorden committed
145
                A.a b:
Tim Steenvoorden's avatar
Tim Steenvoorden committed
146
                String
Tim Steenvoorden's avatar
Tim Steenvoorden committed
147
                (Typed FuncExpr a)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
148
                (Typed TaskFuncExpr (a -> Task b))
Tim Steenvoorden's avatar
Tim Steenvoorden committed
149
                -> Typed TaskContExpr (a -> Task b)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
150 151 152
            )
            <<@@@ HideIfOnlyChoice
        ]
Tim Steenvoorden's avatar
Tim Steenvoorden committed
153
    , DynamicConsGroup "Editors"
Tim Steenvoorden's avatar
Tim Steenvoorden committed
154 155 156 157
        [ functionConsDyn "Apply" "apply"
            ( dynamic \(Typed taskFunc) (Typed expr) ->
              Typed (Apply taskFunc expr) ::
                A.a b:
Tim Steenvoorden's avatar
Tim Steenvoorden committed
158 159
                (Typed TaskFuncExpr (a -> Task b))
                (Typed Expr a)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
160
                -> Typed TaskExpr (Task b)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
161
            )
Tim Steenvoorden's avatar
Tim Steenvoorden committed
162
        , functionConsDyn "EnterInfo" "enter information"
Tim Steenvoorden's avatar
Tim Steenvoorden committed
163
            ( dynamic \s (Typed ty) ->
Tim Steenvoorden's avatar
Tim Steenvoorden committed
164
              Typed (EnterInfo s ty) ::
Tim Steenvoorden's avatar
Tim Steenvoorden committed
165
                A.a:
Tim Steenvoorden's avatar
Tim Steenvoorden committed
166
                String (Typed Ty a)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
167
                -> Typed TaskExpr (Task a)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
168 169
            )
            <<@@@ applyHorizontalClasses
Tim Steenvoorden's avatar
Tim Steenvoorden committed
170
        , functionConsDyn "ViewInfo" "view information"
Tim Steenvoorden's avatar
Tim Steenvoorden committed
171
            ( dynamic \s ->
Tim Steenvoorden's avatar
Tim Steenvoorden committed
172
              Typed (ViewInfo s) ::
Tim Steenvoorden's avatar
Tim Steenvoorden committed
173
                A.a:
Tim Steenvoorden's avatar
Tim Steenvoorden committed
174 175
                String
                -> Typed TaskFuncExpr (a -> Task a)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
176 177
            )
            <<@@@ applyHorizontalClasses
Tim Steenvoorden's avatar
Tim Steenvoorden committed
178
        , functionConsDyn "UpdateInfo" "update information"
Tim Steenvoorden's avatar
Tim Steenvoorden committed
179
            ( dynamic \s ->
Tim Steenvoorden's avatar
Tim Steenvoorden committed
180
              Typed (UpdateInfo s) ::
Tim Steenvoorden's avatar
Tim Steenvoorden committed
181
                A.a:
Tim Steenvoorden's avatar
Tim Steenvoorden committed
182 183
                String
                -> Typed TaskFuncExpr (a -> Task a)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
184 185 186
            )
            <<@@@ applyHorizontalClasses
        ]
Tim Steenvoorden's avatar
Tim Steenvoorden committed
187
    // ordinary (non-task) expressions
Tim Steenvoorden's avatar
Tim Steenvoorden committed
188
    , DynamicCons
Tim Steenvoorden's avatar
Tim Steenvoorden committed
189
        $ functionConsDyn "EqV" "equal" (dynamic \i -> Typed (EqV (VInt i)) :: Int -> Typed FuncExpr Int)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
190 191
        <<@@@ applyHorizontalClasses
    , DynamicCons
Tim Steenvoorden's avatar
Tim Steenvoorden committed
192
        $ functionConsDyn "GrtV" "greater" (dynamic \i -> Typed (GrtV (VInt i)) :: Int -> Typed FuncExpr Int)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
193 194
        <<@@@ applyHorizontalClasses
    , DynamicCons
Tim Steenvoorden's avatar
Tim Steenvoorden committed
195
        $ functionConsDyn "LessV" "less" (dynamic \i -> Typed (LessV (VInt i)) :: Int -> Typed FuncExpr Int)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
        <<@@@ applyHorizontalClasses
    , DynamicCons
        $ functionConsDyn "int" "enter an integer:" (dynamic \i -> Typed (Int i) :: Int -> Typed Expr Int)
    , DynamicCons
        $ functionConsDyn "bool" "enter a boolean:" (dynamic \b -> Typed (Bool b) :: Bool -> Typed Expr Bool)
    , DynamicCons
        $ functionConsDyn "string" "enter a string:" (dynamic \s -> Typed (String s) :: String -> Typed Expr String)
    , DynamicCons
        $ functionConsDyn "tuple" "enter tuple:"
            ( dynamic \(Typed a) (Typed b) ->
              Typed (Tuple a b) ::
                A.a b:
                  (Typed Expr a) (Typed Expr b) -> Typed Expr (a, b)
            )
    , DynamicCons
        $ functionConsDyn "fst" "fst" (dynamic \(Typed (Tuple a _)) -> Typed a ::  A.a b: (Typed Expr (a, b)) -> Typed Expr a)
        <<@@@ applyHorizontalClasses
    , DynamicCons
        $ functionConsDyn "snd" "snd" (dynamic \(Typed (Tuple _ b)) -> Typed b ::  A.a b: (Typed Expr (a, b)) -> Typed Expr b)
        <<@@@ applyHorizontalClasses
    , DynamicCons
        $ functionConsDyn "==" "=="
            ( dynamic \(Typed a) (Typed b) ->
              Typed (Eq a b) ::
                A.a:
                  (Typed Expr a) (Typed Expr a) -> Typed Expr Bool
            )
        <<@@@ applyHorizontalClasses
    , DynamicCons $ customEditorCons "Int" "(enter integer)" intEditor <<@@@ HideIfOnlyChoice
    , DynamicCons $ customEditorCons "Bool" "(enter boolean)" boolEditor <<@@@ HideIfOnlyChoice
    , DynamicCons $ customEditorCons "String" "(enter string )" stringEditor <<@@@ HideIfOnlyChoice
Tim Steenvoorden's avatar
Tim Steenvoorden committed
227
    // type specifications for enterInformation
Tim Steenvoorden's avatar
Tim Steenvoorden committed
228 229 230 231 232 233 234 235 236 237 238
    , DynamicCons $ functionConsDyn "Ty.Int" "Int" (dynamic Typed (Ty VInt) :: Typed Ty Int)
    , DynamicCons $ functionConsDyn "Ty.Bool" "Bool" (dynamic Typed (Ty VBool) :: Typed Ty Bool)
    , DynamicCons $ functionConsDyn "Ty.String" "String" (dynamic Typed (Ty VString) :: Typed Ty String)
    , DynamicCons
        $ functionConsDyn "Ty.Tuple" "Tuple"
            ( dynamic \(Typed (Ty toValue1)) (Typed (Ty toValue2)) ->
              Typed (Ty \(x, y) -> VTuple (toValue1 x) (toValue2 y)) ::
                A.a b:
                  (Typed Ty a) (Typed Ty b) -> Typed Ty (a, b)
            )
        <<@@@ applyHorizontalClasses
Tim Steenvoorden's avatar
Tim Steenvoorden committed
239 240 241
    ]


Tim Steenvoorden's avatar
Tim Steenvoorden committed
242 243
// Helpers //

Tim Steenvoorden's avatar
Tim Steenvoorden committed
244 245 246 247 248 249
derivedType :: Typed Ty a | iTask a
derivedType = case dynToValue of
  (toValue :: a^ -> Value | iTask a^) = Typed (Ty toValue)
where
  dynToValue = dynamic ()

Tim Steenvoorden's avatar
Tim Steenvoorden committed
250

Tim Steenvoorden's avatar
Tim Steenvoorden committed
251 252
intEditor :: Editor Int
intEditor = gEditor{|*|}
Tim Steenvoorden's avatar
Tim Steenvoorden committed
253

Tim Steenvoorden's avatar
Tim Steenvoorden committed
254 255
boolEditor :: Editor Bool
boolEditor = gEditor{|*|}
256

Tim Steenvoorden's avatar
Tim Steenvoorden committed
257 258
stringEditor :: Editor String
stringEditor = gEditor{|*|}
259

260 261
applyHorizontalClasses = ApplyCssClasses ["itasks-horizontal", "itasks-wrap-width", "itasks-panel"]

Tim Steenvoorden's avatar
Tim Steenvoorden committed
262

Tim Steenvoorden's avatar
Tim Steenvoorden committed
263 264
// Evaluation //////////////////////////////////////////////////////////////////

Tim Steenvoorden's avatar
Tim Steenvoorden committed
265
evalTaskConstExpr :: TaskExpr -> Task Value
Tim Steenvoorden's avatar
Tim Steenvoorden committed
266
evalTaskConstExpr (EnterInfo msg (Ty toValue)) = enterInformation msg [] @ toValue
Tim Steenvoorden's avatar
Tim Steenvoorden committed
267
evalTaskConstExpr (Apply taskFunc expr) = evalTaskFuncExpr taskFunc $ evalExpr expr
Tim Steenvoorden's avatar
Tim Steenvoorden committed
268
evalTaskConstExpr (Then task taskFunc) = evalTaskConstExpr task >>= evalTaskFuncExpr taskFunc
Tim Steenvoorden's avatar
Tim Steenvoorden committed
269 270 271
evalTaskConstExpr (Or task1 task2) = evalTaskConstExpr task1 -||- evalTaskConstExpr task2
evalTaskConstExpr (And task1 task2) = evalTaskConstExpr task1 -&&- evalTaskConstExpr task2 @ \(a, b) -> VTuple a b
evalTaskConstExpr (When task1 options) = evalTaskConstExpr task1
Tim Steenvoorden's avatar
Tim Steenvoorden committed
272 273 274
  >>* [ OnAction (Action name) (ifValue (test pred) (evalTaskFuncExpr cont))
      \\ {name, pred, cont} <- options
      ]
Steffen Michels's avatar
Steffen Michels committed
275
where
Tim Steenvoorden's avatar
Tim Steenvoorden committed
276
  test pred (VInt i) = case pred of
Tim Steenvoorden's avatar
Tim Steenvoorden committed
277 278 279 280
    LessV (VInt j) -> i < j
    GrtV (VInt j) -> i > j
    EqV (VInt j) -> i == j

Tim Steenvoorden's avatar
Tim Steenvoorden committed
281
  test pred (VBool i) = case pred of
Tim Steenvoorden's avatar
Tim Steenvoorden committed
282 283 284 285
    EqV (VBool j) -> i == j
    LessV (VBool j) -> False
    GrtV (VBool j) -> False

286 287

evalTaskFuncExpr :: TaskFuncExpr Value -> Task Value
Tim Steenvoorden's avatar
Tim Steenvoorden committed
288 289 290 291
evalTaskFuncExpr (ViewInfo p) (VInt i) = (viewInformation p [] i @ VInt) <<@ ApplyLayout arrangeHorizontal
evalTaskFuncExpr (ViewInfo p) (VBool b) = (viewInformation p [] b @ VBool) <<@ ApplyLayout arrangeHorizontal
evalTaskFuncExpr (ViewInfo p) (VString s) = (viewInformation p [] s @ VString) <<@ ApplyLayout arrangeHorizontal
evalTaskFuncExpr (ViewInfo p) (VTuple a b) =
Tim Steenvoorden's avatar
Tim Steenvoorden committed
292
  ( viewInformation p [] ()
Tim Steenvoorden's avatar
Tim Steenvoorden committed
293 294
    ||- evalTaskFuncExpr (ViewInfo "") a
    -&&- evalTaskFuncExpr (ViewInfo "") b
Tim Steenvoorden's avatar
Tim Steenvoorden committed
295 296 297
    @ \(a, b) -> VTuple a b
  )
    <<@ ApplyLayout arrangeHorizontal
Tim Steenvoorden's avatar
Tim Steenvoorden committed
298 299 300 301
evalTaskFuncExpr (UpdateInfo p) (VInt i) = (updateInformation p [] i @ VInt) <<@ ApplyLayout arrangeHorizontal
evalTaskFuncExpr (UpdateInfo p) (VBool b) = (updateInformation p [] b @ VBool) <<@ ApplyLayout arrangeHorizontal
evalTaskFuncExpr (UpdateInfo p) (VString s) = (updateInformation p [] s @ VString) <<@ ApplyLayout arrangeHorizontal
evalTaskFuncExpr (UpdateInfo p) (VTuple a b) =
Tim Steenvoorden's avatar
Tim Steenvoorden committed
302
  ( viewInformation p [] ()
Tim Steenvoorden's avatar
Tim Steenvoorden committed
303 304
    ||- evalTaskFuncExpr (UpdateInfo "") a
    -&&- evalTaskFuncExpr (UpdateInfo "") b
Tim Steenvoorden's avatar
Tim Steenvoorden committed
305 306 307 308
    @ \(a, b) -> VTuple a b
  )
    <<@ ApplyLayout arrangeHorizontal

309 310

evalExpr :: Expr -> Value
Tim Steenvoorden's avatar
Tim Steenvoorden committed
311 312 313
evalExpr (Int i) = VInt i
evalExpr (Bool b) = VBool b
evalExpr (String s) = VString s
314
evalExpr (Tuple fstExpr sndExpr) = VTuple (evalExpr fstExpr) (evalExpr sndExpr)
Tim Steenvoorden's avatar
Tim Steenvoorden committed
315
evalExpr (Fst expr) = fst
316
where
Tim Steenvoorden's avatar
Tim Steenvoorden committed
317
  (VTuple fst _) = evalExpr expr
Tim Steenvoorden's avatar
Tim Steenvoorden committed
318
evalExpr (Snd expr) = snd
319
where
Tim Steenvoorden's avatar
Tim Steenvoorden committed
320
  (VTuple _ snd) = evalExpr expr
Tim Steenvoorden's avatar
Tim Steenvoorden committed
321
evalExpr (Eq expr1 expr2) = VBool $ evalExpr expr1 === evalExpr expr2