+hoistEither :: Either String b -> Tracer b
+hoistEither = Tracer . ExceptT . return
+
+-- | Evaluates an expression with the current environment inside the tracer.
+evalI :: Expr -> Tracer Val
+evalI e = get >>= hoistEither . flip runEval (eval e)
+
+-- | Traces a program and returns a list of steps taken throughout its execution.
+-- Thanks to lazy evaluation though, the steps can be "streamed" and so this can
+-- be used to debug non-terminating and recursive programs!
+trace :: Statement -> [Step]
+trace x =
+ let f = runTracer (exec x)
+ (_result, effects) = fst $ flip runState mempty $ runWriterT $ runExceptT f
+ in effects
+ where
+
+ exec :: Statement -> Tracer ()
+ exec s = logStatement s >> go s
+
+ -- | Records the statement's execution in the Tracer monad
+ logStatement :: Statement -> Tracer ()