dart_interactive
A lot of sibling languages have a REPL, and is quite helpful in everyday usage, while Dart did not have it (even though it was the 8th highest-voted request). So here it comes!
? Features
A full-featured REPL (interactive shell), with:
- Use any third-party package freely
- Auto hot-reload code anywhere, with state preserved
- Supports full grammar in REPL
- Play with existing code side-by-side
? Demo
Demo 1: Demonstrate features
- Use 3rd party package
- Auto hot-reload
- Support full grammar
Demo 2: Sample workflow
Surely, you do not have to use it like this. It is just a workflow that I personally feel comfortable when working with IPython/Juypter.
Suppose we have my_app.dart
with some code, probably edited inside an IDE:
Play with it a bit:
Then we realize something wrong and want to change it:
(change "Tom" to "Alex" inside `my_app.dart`)
Continue playing with it (auto hot reloaded, and state preserved):
We can also use all dependencies in the package as well, since the REPL code is just like a normal code file in this package.
? Getting started
Install (just standard procedure of installing global dart packages):
dart pub global activate interactive
Use (just a normal binary):
interactive
And play with it ?
Detailed functionality list
Expressions
Statements
(All methods, not only print
)
Functions
Define and redefine
Use local and global variables
Classes
Define and redefine, preserving states
Remark: This follows the Dart hot reload semantics.
Extends and implements
Use local variables, fields, and global variables
Add libraries as dependency
Use !dart pub add package_name
, just like what is done in Python (Jupyter/IPython).
Imports
Built-in package
Third party package
Note: If it has not been added to dependency, please follow instructions above and use !dart pub add path
to add it.
Multiple in one go
Multi line if not ended
(The ...
, instead of >>>
, appears in the two lines, because the package detects it is not finished.)
Run commands
Use prefix !
.
Execute within environment of existing package
interactive --directory path/to/your/package
Implementation
General:
- Create a blank package and an isolate as execution workspace
- Extract imports/classes/functions/etc using analyzer, with replacing when it has the same name, and synthesize a dart file – thus supports rich Dart feature
- Trigger Dart’s hot-reload after the dart file is updated
- Use analyzer to distinguish expressions/statements/compilation-units and do corresponding transformation
- The only thing to let Dart VM service to evaluate is
generatedMethod()
, and do not evaluate anything more - Adding dependencies is as simple as running standard shell command
As for “global” variables:
- Indeed implemented by a field variable
- Statements: Make it inside
extension on dynamic { Object? generatedMethod() { ...the statements... } }
to access it seamlessly - Functions: Convert functions to extension methods on dynamic to access it seamlessly
- Classes: Synthesize getters/setters in classes, and delegate to the field variables, whenever there is a potential access to global variable to access it seamlessly
TODO more implementation discussions if people are interested (above is so brief)
✨ Contributors
Thanks goes to these wonderful people (emoji key):
fzyzcjy? ? ? | Vyacheslav Egorov? | Andreas Kirsch? | Maksim Lin? |
More specifically, thanks for all these contributions:
- @mraleph (Dart team): Pointing out Dart exposes hot reload and expression evaluation.
- @BlackHC: Prior proof of concept and article on the problem of creating a REPL.
- @maks: Prior prototype as an update-to-Dart-2 of @BlackHC’s prototype.