Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
Source expression evaluator #741
We'd like to be able to inpect arbitrary expressions in the source code of running programs in CodeWorld. Starting with GHC 8.6 and with some hacking in GHCJS, this might be possible. Here's the idea.
Part 1: Make the expressions available.
In a source plugin, we could promote all subexpressions in the original source file to new top-level symbols. This would generate a potentially very large number of top-level exposed symbols, but that's okay! It's a debugging feature. As long as the code doesn't slow down enough to become non-functional, we're okay with sacrificing performance.
There are some caveats here. Many subexpressions will have free variables bound in a case, lambda, or pattern or function binding containing this expression. These lambdas will need to be preserved, turning an expression with a non-function type into an expression with a function type. That's okay, too. There are some tricky cases: if this expression uses a variable defined in a
Expressions that are closed, or whose free variables are fully defined in some containing scope without depending on binders, need not be enclosed in lambdas at the top level... but they can be. @luite in particular was interested in being able to adjust the literal numbers in an expression, and see in real time what effect this has on the running program. Interesting idea, but it's not clear where to stop!
Part 2: Generate a lookup tree from source location to expression.
Since we know the source range for each expression from the parse tree, we can also generate a lookup tree that points from each range of the source file that is part of an expression to the synthetic top-level symbol that was defined for it. This lookup tree allows us to navigate from source positions to the corresponding runtime values of those expressions.
Part 3: Build a type class for debug-representable types, and derive it everywhere.
The debug UI can be built compositionally from the types of the values. Each type has at least an output representation - such as a text or picture. As many types as possible have input representations as well. Function types whose arguments all have input representations can be represented using interactive controls that allow the user to adjust inputs and see the corresponding outputs. (Functions with more complex inputs might have to fall back to a dumber representation.)
We will define a type class to capture this logic, and have instances for all relevant types built in or derived during compilation. The deriving logic should have access to function parameter names, so it's a little more complex than typical structural deriving.
Part 4: Build an inspection UI on top of this type class.
Using these parts, we can add features to CodeWorld to inspect arbitrary expressions. For instance, when a user selects a range of the source code, we can find the smallest expression in the source that includes all expressions inside the selected range, and display it in a popup window.
There's a hard UX question about how this interacts with the existing Inspect dialog for pictures. There are copious opportunities to link the two, but students may get confused when they are shown two very closely related recursive structures: one for the source code, and the other for the built-up picture value and its overlays and transformations.