5 ms·
Java/JVM languages, C#/CLR languages, Python and Ruby (minus real threads, but compare to Racket with not-the-best real threading story), NodeJS (minus the "lar
by evdubs 5y ago
Java/JVM languages, C#/CLR languages, Python and Ruby (minus real threads, but compare to Racket with not-the-best real threading story), NodeJS (minus the "large" standard library, but compare to Scheme with a small standard library), can probably include Go
- reikonomusha 5y agoI don't think any of these have REPL-driven development. They might have a "REPL" but not interactive code reloading as a part of the development cycle as is meant by a Lisp REPL.
- School-Cotton 5y agoHow does 'REPL-driven developent' work, exactly? Are you writing code in files and then testing it in the REPL? Or writing substantive code in the REPL and then copying it to files somehow?
- oblio 5y agoFrom what I've seen, the cycle is a bit like this: - launch the program you want to run and have it running in the background - text editor open - bits of code in editor loaded in the running program through text selection and sending them to the be loaded (the most common scenario is doing this in Emacs) However my concern with this is... if you don't run the entire "code frozen" program at once, how can you guarantee the internal consistency of the program if over time you kind of haphazardly add bits of code to it? Maybe you loaded that function, maybe you didn't, did you add that dependency function in the correct order and at the correct "version" (where version is used loosely, every time you type in something, it's a new "version"). I'd really love for someone experienced with Lisp to describe their workflow and also maybe clarify how they handle this (in my opinion, huge) problem of possible program inconsistent states.
- synthc 5y agoFor Clojure there are libraries to refresh the program: these stop the app, reload and evaluate all the source code files, and restart the app to ensure you are working with a consistent state that reflects the source code. Between such reloads you might end up in inconsistent states, but you benefit from fast iteration: My workflow is to update functions in the source code, send to to the repl, quickly test them in the repl, and once done, I save, commit and push the code. CI then runs the test suite from the source files.
- ravi-delia 5y agoI guess it just doesn't really come up that much? I personally aim to have whatever I'm not currently working on written to disk, and very rarely redefine things in the REPL once I've written them to source. Other than that every time you change anything it's automatically propagated, with a few known exceptions (macros mainly). I can say I've never wound up in an unknown state, and I'll keep a slime server running for days.
- mikelevins 5y agoIt's how I've worked day-to-day for thirty years or so, so here's my take on it: I generally work in Emacs. I'll have one or more source files open, plus at least one REPL buffer. The REPL is my communication channel with the running Lisp. The object of the game is to teach the Lisp interactively how to be the program I want. It's already a working program; it just isn't the one I want to build. I'll use Lisp expressions to teach it, one expression at a time, how to be what I want. The contents of the source files depend on the stage I'm at with a program. If I'm just starting then it's probably one file with a few snippets that I C-x C-e to send to the REPL to modify the dynamic environment of the running Lisp. If it's a more mature project, then I have a bunch of source files that fit into an ASDF system (which defines sources to build and dependencies among them and any libraries they depend on). The system as a whole will be loaded into the memory of the running Lisp, converting it into a work-in-progress version of my app. I'll have some subset of the project files open in buffers so that I can add or edit definitions and C-x C-e them into the running Lisp and see in the REPL whether my changes have the effect that I intend. I continue this way indefinitely, teaching the running Lisp expression-by-expression the features that I intend for the finished product to have. When the difference between my idea and what the Lisp does shrinks to epsilon, the program is implemented. I run a build function and I have a delivered application. When something goes wrong during my work, I land in a breakloop, which is something like a backtrace, except live. Rather than a printout of an unwound stack, it's a live REPL on the dynamic environment of the stack at the moment the error occurred. I can use the breakloop to wander up and down the stack, inspect and edit variable, type, and function definitions, and resume execution at the moment of the error when I'm ready to see the effect of my changes. The Lisp then runs the same function from where it broke, except that the variables, types, and functions I modified now have the definitions I just gave them, rather than the ones they originally had. If I change the definition of some class that already has live instances, then the Lisp reinitializes those instances with the new class definition and continues. If I neglected to show it how to reinitialize those classes, it drops me into another breakloop and offers me the opportunity to tell it how to do so, after which it will continue, as before. If I stumble across something curious and I want to show it to a collaborator in context, I can save a heap image of the running Lisp and give it to my colleague. That colleague can start the image on their machine and see the same dynamic environment that I saw. In some older Lisps, that dynamic state could include all of the windows on my screen, and their contents, and which one was the active window, and where the text-insertion point was. I can deploy my work-in-progress apps to a staging machine with a repl server built in. I can let it run in a testing environment indefinitely, and use Emacs to connect to a live REPL that I can use to rummage around in the running app, inspect and edit variables, types, and functions, and generally do anything I would do if it were running in my normal dev environment. If I see something odd, I can again dump a heap image, copy it back to my development machine, and crawl around in its running state while I let the deployed test version go back to doing what it was doing. There's quite a bit more to it, but with luck, that gives a general idea of what the workflow is like. Common Lisp environments are generally like this; some have richer sets of affordances than others. Smalltalks are like that, too, and so is Factor. Most other Languages and repls are not so much like that. Clojurescript with figwheel gives you part of it, and the part it implements is pretty good.
- stevesimmons 5y agoPython with a Jupyter notebook is a very solid REPL development environment: - Prototype code in Jupyter. Write first as a few lines of code per cell. Then merge into functions. Focus is on 5-10 cells becoming a single function. - When working, copy the functions back into the main Python file. - Periodically reimport the main Python file back into Jupyter and recalc the cells currently in scope. - Repeat... - The main Python file ends up mostly like an API. And the Jupyter notebook as docs on how to call that API.
- medo-bear 5y agoi agree that python's repl environment is nice. i worked with python and jupyter for much longer than with common lisp. actually due to my domain it is still my main language. however i was taken aback when i first saw repl development in lisp. it is just far more seemless and stable. how often do you need to do python kernel restarts in jupyter? what i found extremely surprising is the huge performance difference between sbcl and cpython. common lisp as implemented in sbcl produces some of the most performant computations out there. also writing code in s-expressions turned out to be an unexpected killer feature for me. finally something you cannot do in python that is built into lisp is being able to debug and live edit a running (production) image. while i continue to use python the same is no longer true for jupyter. i think using org-babel in emacs is a much more superior experience, with the added benefit of having the whole development environment available to you
- kaba0 5y agoWell, not the same thing but Java can hot-reload classes (to a degree depending on implementation). But what truly makes repl-driven development in lisps so good is the more functional approach to development. Hot reload gets better the smaller the replaceable units get. Lisps have it good with basically paren-level hot reload.