4 ms·
Reple: “Replay-Based” REPLs for Compiled Languages
- joe_the_user 8y agoA shell that reruns the entirety of a compiled language each time you enter a line. "This has obvious performance disadvantages if you're actually doing computation inside your REPL instance—for each new line, you'll have to redo all old computation. However, this technique has other distinct advantages that make it very desirable for certain use cases." Are there any cleaver increment compile/run tricks that could speed this up?
- xiii1408 8y agoYes, there are tools like rcrl (mentioned in the blog post) that dynamically link in new pieces of code [1]. The author of rcrl gave a nice talk at CppCon [2]. The trouble with tools like that is that they're pretty language specific, so it's my impression that it would require a fair bit of work to generalize to languages other than C++. [1] https://github.com/onqtam/rcrl https://github.com/onqtam/rcrl [2] https://www.youtube.com/watch?v=UEuA0yuw_O0 https://www.youtube.com/watch?v=UEuA0yuw_O0
- yorwba 8y agoInstead of putting all lines into one big chunk of code that is recompiled and rerun every time, one could instead compile it into a dynamic library with a single function that gets the current variable assignments passed as arguments, executes the single line of code and then waits for input to generate the next dynamic library, which it then loads and calls with the new variable assignment. However, that solution would require the same level of language-specific knowledge as the modification mentioned below "Limitations" in the blog post. That's also why I never implemented the idea, despite having carried it around in my head for a few years now. Hopefully someone else will eventually get around to implementing it.
- pankajkumar229 8y agoCould it be done in llvm or another backend so all frontend languages can benefit?
- evmar 8y agoThis is how http://neugierig.org/software/c-repl/ http://neugierig.org/software/c-repl/ worked. (See "How it works" in the README: https://github.com/evmar/c-repl/blob/master/README https://github.com/evmar/c-repl/blob/master/README )
- rgoulter 8y agoI made use of ANTLR4 for parsing C to write a "worksheet" (like Swift's Playgrounds) tool: https://github.com/rgoulter/c-worksheet-instrumentor https://github.com/rgoulter/c-worksheet-instrumentor It's an example of what you can do with "specific to a language", although it'd suffer the same limitation of "slower after a big computation".
- dawg 8y agoThis approach is taken by https://github.com/dlang-community/drepl https://github.com/dlang-community/drepl and https://docs.scala-lang.org/overviews/repl/overview.html https://docs.scala-lang.org/overviews/repl/overview.html. While it works surprisingly well, you need to parse and transform the REPL input to emulate some language constructs in the right scope.
- abecedarius 8y agoIt's certainly possible, but another thing you can often do is just not depend on slow computations during interactive development. This is how I usually work on code in Python: with https://github.com/darius/halp https://github.com/darius/halp I have repl-like examples in my source code, and Emacs reruns them all at a keystroke and tells you if any output changed; mostly it's no trouble to keep helpful examples that can all be rerun in a small fraction of a second. IPython is fancier and can be used with expensive computations, so this might be a dead end, but I didn't want to have to think about whether the state of the examples was ever out of sync with a fresh reexecution of the code (and also I wrote this a long time ago). I thought I might get to cleverer incremental update methods eventually, but it hasn't been worth the trouble to try when the above workflow has worked well enough.
- lispm 8y agoThe believe that REPLs are based on interpreters is strange. The Lisp read-eval-print-loop has been using any mix of compiled and interpreted versions for several decades. Something like ECL can compile Lisp incrementally to C, calls the C compiler and loads the compiled code into Lisp. SBCL for a long time only had a compiler. The compiler then either compiles to memory or to files. These files can be loaded into Lisp. These system maintain much of the state between compiles. There is zero reason why EVAL can't be implemented by direct-to-machine-code compiler - like in SBCL. The SBCL Read-Eval-Print-Loop: * (flet ((my-function (a) (+ a 42))) (disassemble (function my-function)) (my-function 0)) ; disassembly for (FLET MY-FUNCTION) ; Size: 37 bytes. Origin: #x1002FC5E98 ; 98: 498B4D60 MOV RCX, [R13+96] ; no-arg-parsing entry point ; thread.binding-stack-pointer ; 9C: 48894DF0 MOV [RBP-16], RCX ; A0: 48895DE8 MOV [RBP-24], RBX ; A4: BF54000000 MOV EDI, 84 ; A9: 488BD3 MOV RDX, RBX ; AC: FF1425A000B021 CALL QWORD PTR [#x21B000A0] ; GENERIC-+ ; B3: 488B5DE8 MOV RBX, [RBP-24] ; B7: 488BE5 MOV RSP, RBP ; BA: F8 CLC ; BB: 5D POP RBP ; BC: C3 RET 42 If one wants to execute whole programs, this is also what Lisp usually supports. One types to an editor and compile-and-load-buffer is a typical operation. This first compiles the buffer and then loads the code into the current Lisp. Sometimes output goes to temporary windows. Some Lisp IDEs queue the editor buffer expressions into a REPL for execution and may compile them piece by piece. There is usually very little use for modifying existing output for changes - though some implementations can do that - like Symbolics Genera, where output is recorded and deltas can computed to generate minimum updates. This can be used in custom applications, which have a command loop and an output recording window. Usually this is not done, because both the internal and the external state is changing so much that in general updating the REPL output is useless. Might as well clear the existing command loop / repl output and generate fresh output.
- mamcx 8y agoThis is a thing that interest me much (I'm building a relational lang in rust), I haven't found enough info or help (I have asked like a dozen times already!) in how do it with compiled languages (the answer is always "do read this large codebase that solve it)". If I understand you well, this demand the compiler to be a JIT? And be responsible to emit and understand assembler? Is possible to make a generalized solution, that for example, could work for a transpiler? Is viable for a compiled language in multiple platforms or arches without a huge codebase? What could be a minimal example that could work?
- rgoulter 8y agoIn terms of "value for effort", this sounds like a cool idea. I'd guess this approach also struggles with multiple-line input like if/else, loops, functions. I'm reminded of https://github.com/jcartledge/sublime-worksheet https://github.com/jcartledge/sublime-worksheet which is language-agnostic on the other end of things. The two ideas might work well together in limited cases.
- xiii1408 8y agoIt's actually pretty easy to detect multi-line inputs as long as they have clear enclosers (like '{}' for a block in C/C++). This is what reple does. This doesn't require a parser--you just have to know what the enclosers are (one of the things that goes in a reple config file) and then count them to see if they're matched or not. For languages without Python-like syntax or with enclosers that can be ambiguous (some dialects of Fortran), this would be pretty difficult.
- mark_l_watson 8y agoOK for some use cases, but not mine. I often run long running computations in repos, and like to be able to add/modify code. A replay based repl would not work at all for me.