7 ms·
Having used Clojure and Common Lisp professionally, I think advocates for REPL driven programming universally overstate the utility of a first rate REPL. Yes,
by ashtonkem 6y ago
Having used Clojure and Common Lisp professionally, I think advocates for REPL driven programming universally overstate the utility of a first rate REPL.
Yes, it’s nicer than Python’s. Yes, it’s convenient. No, I never ended up doing my development in the REPL first. Why? Because editing mistakes in a REPL usually sucks, because a REPL is not a text editor.
What I ended up using heavily was REPL to file integration, which gave me the ability to write a function normally, evaluate it in the attached REPL session, and then play around with it in the REPL. This is far short of the “REPL driven development” that’s commonly discussed, and frankly something that’s probably possible with the Python REPL if they wanted to.
Editing data and functions in the REPL is a neat trick, but it’s a double edged sword, because a REPL can crash, and it provides incredibly rudimentary support for diffing current state and migrating changes back to permanent files. I would never start with anything more than a trivial “how do I manipulate this list” in the REPL for that reason. Oh, and we had a lot of issues with REPLs getting into a bad state with Clojure due to multi methods and protocols; if you’re doing your primary work in the REPL, then having to restart it due to it becoming unstable really sucks.
- mikelevins 6y agoAs I said elsewhere, when I say "repl-driven programming", I do not mean "programming in a repl window", or "programming at a command shell". I'm talking about the runtime's read-eval-print loop, not the UI's repl window. When I'm working, there is little or no migrating of code from the repl buffer to a file because it's already all in a file. I almost always work in a file. I write snippets in a file, send them to the repl with a keystroke, and build up the world incrementally as I discover what it needs to be. As the contents of the file get larger and more complicated, I move things around and organize them. It's a conversation with the repl, not an editing session in the repl window. I don't consider any Clojure tools I know of to constitute a proper repl-driven environment, precisely because the language and runtime lack support for the kinds of programming and debugging that I've taken for granted for decades. If I can't inspect and edit and redefine everything in the runtime, it's not the full, proper set of tools. I've written a good bit of Clojure code, and I'll happily do it again if I need to do something that isn't convenient in, say, Common Lisp, but I consider it an acceptable alternative to things like Haskell and Scala and F# and Swift, not an attractive alternative to Lisp and Smalltalk. I do occasionally need to restart a Lisp environment because of some gnarly breakage I've committed, but it's pretty rare. Moreover, killing and restarting my favorite lisps takes about--wait, let me check--okay, a second and a half to kill a live app in staging and have it back up, fully-functioning.
- dkersten 6y agoYeah, outside of simple use cases, I mainly interact with the clojure REPL by evaluating forms directly from my editor. It’s about being able to incrementally build something while having it running and updated as you go. I also tend to often just reload changed namespaces and resetting the (component/mount/integrant based) application too. Or letting shadow-cljs live reload my clojurescript. But having the REPL directly accessible is still super useful.
- svnpenn 6y agoIf your code is in a file, and you're running the code from the file, that's not REPL. that's interpreting or compiling a file. REPL is opening interactive mode (if the language supports it) and entering "2+2". You calling anything else REPL is just wrong, misleading and confusing.
- dragonwriter 6y ago> If your code is in a file, and you're running the code from the file, that's not REPL. that's interpreting or compiling a file. It is not “interpreting or compiling a file” if the tool you are using to evaluate code in the file is sending isolated blocks of code to a REPL and not, well, interpreting or compiling the file. > REPL is opening interactive mode (if the language supports it) and entering "2+2". No, a Read-Eval-Print Loop (REPL) is not exclusively used by a user typing keystrokes at a terminal; like other terminal software, it is a candidate for being driven by other programs. > You calling anything else REPL is just wrong, misleading and confusing. You insisting a REPL isn't a REPL when the “Read” part is reading something other than keystrokes directly entered by a human user is just wrong, ignorant, and confused.
- mikelevins 6y ago'REPL is opening interactive mode (if the language supports it) and entering "2+2".' If I'm going to enter "(+ 2 2)" using my normal development tools, I'm probably going to start Emacs and tell it to start a SLIME session with one of the Common Lisps I use. When I do that, it'll create a repl buffer, because that's the way I have slime configured. I mean, I don't have to load slime-repl. I could just interact with the Lisp's read, eval, and print functions directly from an arbitrary buffer without involving the SLIME repl display at all. But, y'know, force of habit. Also, I do occasionally type something in SLIME's repl buffer, especially if I want to see some big wad of output and don't want it spewed into the middle of the expressions I'm working with. Most likely I won't type "(+ 2 2)" in the slime-repl buffer, though. I'm more likely to type it into a scratch buffer and send it to the Lisp by hitting C-x C-e. As soon as I start typing Lisp code, I know I'm probably going to want to add some more and maybe edit it and probably send it to the Lisp again. That's just more convenient if I have it sitting in a buffer in front of me, and not scrolling off the top of the repl buffer into infinity. So am I working in a repl? Lisp is running a loop waiting for input. I'm sending expressions to it. It's reading them, evaluating the s-expressions produced by READ, and then printing the resulting values to a stream I can look at. Sounds like a repl to me. It's what I've always understood a repl to mean, including when I'm building them. Does it count as working in a repl when I use a keystroke to send "(+ 2 2)" to the Lisp from the scratch buffer, or does it only count if I actually physically type the text in the buffer where the Lisp displays its prompt? What if there is no prompt? What if I start Lisp and SLIME but don't load the slime-repl extension? Does that mean that the loop that is reading, evaluating, and printing stops being a repl? I'll probably save my scratch buffer to a file at some point, if there starts to be enough text in it that I think I might forget some of it. Does it stop being an interaction with the repl the instant I save the buffer to a file? I can save the slime-repl buffer to a file, too; does it stop being a repl when I do? What about Lisp and Smalltalk environments where the read-eval-print loop doesn't display a prompt? Take a Smalltalk or INTERLISP worksheet, for example. Is it a repl if there's a loop reading, evaluating, and printing expressions, but no prompt? Does it stop being a repl if the incremental inputs and outputs get saved to a file? Does it count if it's not a text file, but the environment automatically saves the state of the worksheet to an image file that it automatically deserializes the next time you start the environment? I don't think I'll adopt your lexicon, but I am sort of curious where its boundaries are.
- jmchuster 6y agoThis is what I use as my canonical example of REPL-driven development. https://vimeo.com/230220635 https://vimeo.com/230220635 The main thing that stands out to me is playing with small snippets of code in real-time as you're writing it. Contrast that with writing a class and methods, then writing unit tests, then running them. There might as much as ten minutes between the time you start writing your code and when you run any portion of it, and always running large chunks of changes.
- 0x445442 6y agoHaving never developed code professionally in Lisp, my question would be; does the REPL work flow replace test code? Because if the REPL is being used as a replacement for tests, then I can see future readers having a harder time groking the code without the benefit of test driver code to analyze.
- mikelevins 6y agoI'd say it's more like tests grow naturally out of repl interactions. Test-driven development as a discipline emerged from the Smalltalk community, and I don't think that's an accident. I think it's a formalization of the way that Smalltalk and Lisp programmers naturally tended to work. I generally start on something by making a naive model of what I'm trying to accomplish and interrogating it. In successive iterations, I modify the model and the interrogations in the direction of what I need it to be, discovering details along the way. I'd say that a majority of my actual activity is testing. That's one reason I prefer to interact with a repl from a file: because I want a record of my interactions. I want to be able to do them again and again. The difference between that record and formal tests is a simple matter of copying the trail of my interactions into a test framework, and that, too, is how I normally work: make model; test it with interactions; keep the interactions around for later. As I make progress, the models turn into data structures, and the interactions turn into functions and tests.
- _theory_ 6y agoThank you for explaining your process. I think I might benefit from this approach.
- xkeysc0re 6y ago>What I ended up using heavily was REPL to file integration, which gave me the ability to write a function normally, evaluate it in the attached REPL session, and then play around with it in the REPL. This is far short of the “REPL driven development” that’s commonly discussed, and frankly something that’s probably possible with the Python REPL if they wanted to. I mean, yeah. You just import the Python module you're writing and you can call any function or inspect any object you want. iPython even has hot reload
- Folcon 6y agoI'm personally of the opinion that this isn't the same thing and I'm saying this as someone who wants to do this in python. Unless you've figured it out? In which case please say, because I want to code python like this. Specifically I write code in my editor, hit a keystroke and all the code at the cursor gets sent to my running python and evaluated. The editor needs to be aware of python's indenting rules so it can do this, but once again if you've worked out a nice way to do this please say. Minimal keystrokes required would be great =)... I'm not really requiring that the evaluated output gets sent to the editor, though of course that would be ideal, because in the worst case I can side-by-side the editor and the terminal. From my perspective jupyter notebooks get kind of close, but I'm not coding in a file, so it's not quite ideal.
- Izkata 6y ago> Specifically I write code in my editor, hit a keystroke and all the code at the cursor gets sent to my running python and evaluated. The editor needs to be aware of python's indenting rules so it can do this, but once again if you've worked out a nice way to do this please say. Minimal keystrokes required would be great =)... ...I call this "copy/paste". And since I use X clipboard and a tiled window manager, it's only a couple keystrokes and no mouse. Highlight the code in the editor, "super+<direction>" to switch windows, "shift+insert" to paste.
- Folcon 6y agoI don't quite agree, that might work for your specific workflow, but you can flub that sort of thing. There's a reason I'm specifying specific actions, I can easily see increasing the cognitive burden breaking the ease of that workflow. You can't tell me that putting your cursor to a code block and hitting a key is the same as selection, copy, swap to console, paste... I've certainly had situations where I've done what you're describing and then still taken the extra time to setup the workflow I've described above because it's worthwhile when you're trying to debug a knotty problem.
- reddit_clone 6y agoYou don't have to type up stuff in repl literally. You can write regular functions in emacs and send pieces of code to the running repl. For adhoc testing code you can type on the repl buffer.
- blandflakes 6y agoI 100% agree with you and another comment: https://news.ycombinator.com/item?id=25622990 https://news.ycombinator.com/item?id=25622990 I spent years in Clojure and found that writing code in the REPL just... isn't fun. This has been true for every interactive system I've ever used (including... the shell). The shell as a REPL for a succinct language is nice for interactive workflows, but for trying to build less ephemeral pieces of code, the editor is the first class citizen I care about. Sending code to a REPL to be interacted with? Cool, and I think this is what a lot of people mean when they say REPL-driven. I'd imagine convenience around that in a language or its tooling would be what convinces me to do more REPL-style things.
- koz_ 6y agoI think what people mean when they say REPL-driven is to have a headless REPL that one never actually interacts with directly, but rather sends it snippets of code for evaluation directly from one's editor. Typing code into a shell prompt is excruciating by comparison. I think this is a huge barrier of entry to languages like Clojure. The syntax and dynamic nature of the language really lend themselves to an extremely interactive and productive workflow, but that is a hard thing to communicate. It seems most people think that when Clojure programmers refer to the joy of the REPL they imagine writing code into Python or Ruby's REPLs (e.g. https://repl.it/languages/python3 https://repl.it/languages/python3), but that is about as far from what is meant as is possible. I mean, who would enjoy programming in an environment that deleted your work every time you finished a line of code?
- chris_j 6y ago> What I ended up using heavily was REPL to file integration, which gave me the ability to write a function normally, evaluate it in the attached REPL session, and then play around with it in the REPL When I do REPL driven development in Clojure, that's exactly what I do and that's what other folks that I know mean by REPL driven development (in Clojure): define types, functions and variables in a source file, often in a (comment) form, eval them one by one, and copy the code out of the (comment) form when it's stable enough. I wouldn't type code directly into the REPL; that's not a pleasant experience in Clojure - but might be pleasant in Common Lisp or Smalltalk for all I know. The process that I and others use in Clojure most definitely differs from the process that the OP describes and I lack enough familiarity with Common Lisp to know if the process is more pleasant in that language. I assume it is and I must make time to learn Common Lisp properly some day.
- TheAnswerMan 6y agoYou don't even need the "REPL" buffer to do REPL style development. You can just use the "eval" keybinds of your editor to eval the source code in the files. For any test/scratch code, just create a scratch file outside your project and write the experiments there, eval'ing as you type. That file could even be an "official" unit test you include in the project.