37 ms·
What makes a good REPL?
- linkmotif 9y agovi mode!
- yoodenvranx 9y agoMatlab has a nice sort-of-repl feature which I miss in every other language: you can separate the code in a file into several blocks and then execute the current block (the one with which contained the cursor) with ctrl+enter. With this feature you still have the full text editing capabilities but you also have a flexibility you get from a repl.
- mclehman 9y agoLisp + Emacs + SLIME: miss no longer. Picking them all up is a tall order though.
- taeric 9y agoWhat do you mean with "picking them all up?"
- _mhr_ 9y agoThey mean the learning curve is steep.
- taeric 9y agoAh, fair enough. The nice thing is most repl's have the same bindings as each other. So, some basic drills can get you up to speed.
- pklausler 9y agoA "steep learning curve" actually means that the amount learnt grows rapidly over time. It's odd how this basically obvious conceptualization has had its meaning inverted in popular usage.
- taeric 9y agoThis is one of my pet peeves, as well. Though I have moved on. Colloquially, it is clear how folks take that saying. Just transpose the chart if it helps you think about it. That is, it may have originated with that meaning. But it is far from unique in having the general meaning shifted with use.
- pjmlp 9y agoEmacs suffers from not being like Lisp machines REPL, meaning lacking graphical display on buffers. If I remember correctly, XEmacs used to support it (which was my favorite fork), but it seems to have faded away. For example, try to achieve this demo on Emacs. https://www.youtube.com/watch?v=o4-YnLpLgtk https://www.youtube.com/watch?v=o4-YnLpLgtk
- abhirag 9y agoI have never used a Lisp Machine so forgive my naivety but I saw the demo and you seem to be referring to the ability of displaying images inline in a buffer. Emacs can definitely do that, I have used Emacs IPython Notebook[1] which is a REPL supporting this. I could fire up a jupyter notebook and use pillow[2] to recreate the image manipulation part of the demo. [1] -- (https://github.com/millejoh/emacs-ipython-notebook/blob/master/README.rst https://github.com/millejoh/emacs-ipython-notebook/blob/mast...) [2] -- (https://python-pillow.org/ https://python-pillow.org/)
- pjmlp 9y agoThat is partially what I was referring to. The other part, which might not be visible on that video is the integration of debugger into the REPL, and the ability to redo a piece of code after breaking into the debugger and fixing it. So you can do something like, REPL => error (ask to start debugger) => track down and fix error => restart error expression => finish the execution of the original REPL expression with the respective result.
- abhirag 9y agoThe only debugging experience I find enjoyable in Emacs is debugging Clojure using cider[1] and I think it comes close to what you are describing but I think you might already be aware of that :) [1] -- (https://github.com/clojure-emacs/cider/blob/master/doc/debugging.md https://github.com/clojure-emacs/cider/blob/master/doc/debug...)
- bitwize 9y agoSLIME doesn't give you this?
- abhirag 9y agoYou seem to be using SublimeText, check out SublimeREPL extension[https://github.com/wuub/SublimeREPL/blob/master/README.md https://github.com/wuub/SublimeREPL/blob/master/README.md]. Once setup you'll be able to evaluate code blocks in a running REPL :) Also, Emacs modes of most languages with REPLs will have this functionality inbuilt.
- jhgb 9y agoLisp buffers? Smalltalk workspaces? Is this capability any different from those?
- blt 9y agoMatlab's REPL experience is great. It satisfies almost all of the "what makes a good REPL" and "what makes a programming language REPL-friendly" criteria in the article. Having a full featured plotting library available to inspect intermediate values when debugging is amazing.
- auggierose 9y agoI guess Mathematica has the most advanced interface in that respect. That's what inspired all the other repls like Jupyter.
- bitwize 9y agoMost Lisp modes for Emacs have an eval-sexpr-at-point command which allows you to send the current sexpr to the REPL. This is in SLIME for CL but even the most basic Scheme mode has it as well.
- _pmf_ 9y agoClojure's Emacs REPL also has this; it's strange that the article does not mention it, but talks about copying back and forth between REPL and suorce, which I consider a antipattern and really ugly workflow.
- jasonm23 9y agoEmacs can do this with Ruby, Python... Hell, you can get it to eval buffer, region, line with anything that'll accept input and return a useful output. The ease at which one can get any language (or I/O machine) to play along with this workflow in Emacs is astonishing.
- suraj 9y agoJupyter[1] lifted this feature from Matlab & made it work with any kernel. [1]: http://jupyter-notebook.readthedocs.io/en/latest/examples/Notebook/Running%20Code.html http://jupyter-notebook.readthedocs.io/en/latest/examples/No...
- pedrosorio 9y agoI believe it was inspired by Mathematica which created the concept of notebooks with cells backed by a kernel in 1988: https://en.wikipedia.org/wiki/Wolfram_Mathematica#The_Notebook_interface https://en.wikipedia.org/wiki/Wolfram_Mathematica#The_Notebo...
- hamandcheese 9y agoThere's a lot of excitement about compiled languages lately, and many seem to wonder if interpreted languages are dying. Unfortunately I don't see the value of a good REPL brought up in those conversations very often.
- mhluongo 9y agoI like a good interpreted language as much as the next guy, but there are REPLs for compiled languages. Pretty sure Scala has one and maybe Haskell
- taeric 9y agoThough, they are lacking the magic of a REPL in CL. Hotspot replacement in JVM languages is neat compared to the magic of redefinitions in CL. And it seems nobody ever tries hooking a REPL up to a running system anymore.
- deleted 9y ago[deleted]
- mhluongo 9y agoSome of us still do in Clojure land for run-time debugging, though skipping the crazy state mutation stuff I've seen in eg CL.
- pjmlp 9y agoJava 9 is also bringing one.
- taeric 9y agoI'm curious what makes this superior to Beanshell. Or any other style of REPL that you could do on the JVM. Not against the idea, per se, but it seems hardly new ground. And unlikely to be nearly as powerful as a REPL in CL. (Though, again, few things are. Not sure that any are, to be honest.)
- adamnemecek 9y agoI would also add hot loading, aka the ability to change your code and get to the same point in the execution of the code.
- seanmcdirmid 9y agoHot loading only implies the former (change code of a running app) and not the latter (automatically rerun application and return to paused point). Hot loading is typically a deployment feature that doesn't interact with the debugger at all (it doesn't require the program to be paused). Fix and continue (from smalltalk) never put the program back into a good state after code update.
- deleted 9y ago[deleted]
- aidos 9y agoI can't live without auto reloading in ipython. It's not perfect, but I work in a space where I need to load a lot of state before I can do anything meaningful. Being able to change to code inside running objects and maintaining all that state in memory is an absolute god send.
- bpicolo 9y agoI loved this article. I am a huge proponent of development ergonomics, and clojure(script) are really fantastic in this area. Most languages have a pretty similar standard set of tooling, but the pieces that a language does exceptionally well really stand out in cases like this. I'd really love to see what languages designed specifically with ergonomics/tooling in mind look like
- sedachv 9y ago> I'd really love to see what languages designed specifically with ergonomics/tooling in mind look like Warren Teitelman (and later many people at Xerox PARC) did this with BBN Lisp/Interlisp, starting with real paper teletypes through to graphical workstations.
- breatheoften 9y agoQuokka adds a nice repl like experience for JavaScript/typescript to various editors. I'm a fan of their products! Hydrogen is quite nice for python repl development in atom. Hydrogen connected to a remote kernel plus a script to synchronize files to a remote server replaces writing code in Jupyter notebooks for me (I just can't enjoy editing code in a browser ...)
- abhirag 9y agoHydrogen seems nice, I also occasionally get tired of the default jupyter notebook interface and use Emacs[1] to edit my notebooks, this way I can have all the niceties of jupyter notebooks without working in the browser :D [1] -- (https://github.com/millejoh/emacs-ipython-notebook/blob/master/README.rst https://github.com/millejoh/emacs-ipython-notebook/blob/mast...)
- christophilus 9y agoI think REPLs work better in languages that provide 1st class immutability support, since it's easier to set up state once, then play with functions without having to re-set up state every time you tinker.
- lispm 9y agoI think REPLs work better in languages that don't provide 1st class immutability support, since it's easier to change state, then play with functions without having to re-set up state every time you tinker.
- narrowtux 9y agoYou could just store your modified state to new variables though
- yorwba 9y agoOr you could just make copies of state you want to keep before mutating it. Both mutable imperative code and immutable functional code can emulate each other, so which one to choose largely depends on your preferences.
- junke 9y agoIn a live-coding session, how does the existing, running code knows that it should look at the new variables instead of the old ones?
- chrissnell 9y agoPretty-printing of data structures in a human-readable format.
- deleted 9y ago[deleted]
- yorwba 9y agoI think Jupyter has the right idea here with various kinds of repr-alternatives that can render html or images directly into the notebook. http://ipython.readthedocs.io/en/stable/config/integrating.html?highlight=repr#rich-display http://ipython.readthedocs.io/en/stable/config/integrating.h...
- junke 9y agoThis is the kind of use cases where multiple dispatch is useful.
- yorwba 9y agoHow so? I feel like single dispatch (as in Python) is already enough.
- junke 9y agoIf you write a custom _repr_html_, you are likely to assemble subparts by recursively calling _repr_html_ on sub-elements (thanks to open recursion). If you have an object B which inherits from A, you can specialize on B, or use the existing method for A. However, you cannot specializes the "html" part with inheritance. Say for example that you want to define a slightly different _repr_html_ method depending on the context (maybe you target the subset of HTML that works correctly in emails) or if you want to render "latex" with custom "tikz" macros instead of using matplotlib. With multiple dispatch, you can specialize on the target too, which means you can specialize wherever required or fall back to a generic behavior if not.
- justifier 9y agoExportable input and output of session
- agentgt 9y agoWith fast compiling languages a unit test is not for off from being a REPL. In Java I use JUnit as a REPL. Actually I prefer unit tests over a REPL the same reason I prefer bash scripts over one liners or SQL scripts instead of typing into the interpreter... I don't like the ephemeral nature of REPLs. Also with true REPLs unlike the debug unit test approach I mention with Java you really need the language to be dynamic. I'm not entirely sure why but static type languages are not very good at allowing code modification while running (I mean I have ideas but I don't know precisely if there is an actual theoretical limitation). I guess I prefer static analysis over the complete ability to modify the code base while running. Only add my 2 cents because the article doesn't mention any negatives to REPLs.
- pjmlp 9y agoREPLs are not ephemeral on Common Lisp environments. There are/were true REPLs with static languages, Mesa/Cedar and Oberon are two examples that come to mind. On Mesa/Cedar's paper they refer that they wanted to provide the same experience as their Smalltalk and Interlisp-D environments. On Oberon's case, it required just recompiling/reloading a specific module, which given Oberon's compile speed, was pretty quick. .NET has now a REPL, which coupled with Edit-and-Continue (when it works) is also quite good. I used to use Jython/Groovy as my Java REPL, now just have to wait for the Java 9 release.
- agentgt 9y ago> REPLs are not ephemeral on Common Lisp environments. You'll have to patiently elaborate more for me. Do you mean because the editors keep track of it and that you are working with immutable/idempotent stuff? Otherwise IMO it is ephemeral because you are mutating things and you can forget what you have loaded and what not. I'm probably wrong though. > There are/were true REPLs with static languages, Mesa/Cedar and Oberon are two examples that come to mind. Yes many do including my favorite of OCaml's utop but not many allow hot code replacement for a currently running program. I think the author alluded to that. Of course I have no experience with Mesa/Cedar Oberon. I'll have to check those out. > I used to use Jython/Groovy as my Java REPL, now just have to wait for the Java 9 release. I used Groovy as well but mainly because I didn't want to load a full IDE to test a couple of things. As I mentioned before I think with Eclipse/IntelliJ + JRebel + Debug attachment you can get damn close to a REPL. And depending on how you define REPL I think hot code replacement + debugger might actually be more powerful than a REPL but I have to explore that thought some more.
- whipoodle 9y agoI like when you can type part of a line of code, press the up arrow, and have the REPL complete the line from history.
- photojosh 9y ago"Finally, not all programs need be fully automated - sometimes the middle ground between manual and automated is exactly what you want. For instance, a REPL is a great environment to run ad hoc queries to your database, or perform ad hoc data analysis, while leveraging all of the automated code you have already written for your project" This is exactly why I love Python. My Django webapp gets features (DB reports, external API pushes, etc) added as the client's budget allows, and before they are I'll often do them manually. Given that the UI for a new feature is usually the most work, it's been working well. So the process usually goes: REPL/Django shell -> Django management command -> End-user facing feature. I'll grab what I did the first time in the Django shell, and put it into model logic plus a tentative management command. Then the next time I have to do the task I'll make sure the command works properly. And then when the budget allows I'll add access via the UI. Ninja edit: I forgot to mention that `import ipdb; ipdb.set_trace()` is invaluable to get to the point in the HTTP response code where you can start adding new stuff or diagnose errors directly.
- einnjo 9y agoI've dabbed in elixir and clojure for some time now and the ability to gain a REPL into a live system opens many opportunities for live debugging and introspection. It's a great tool when dealing with unknown states and bugs in production.
- ben-schaaf 9y agoI follow a similar procedure for an open source community project in Ruby on Rails. There are a fair amount of rare things that need to get done where it's just easier to do them in a reply on the web server. One of the admins of the community actually learnt enough about the rails DSL to write basic queries through the reply and do some of the things I usually would.
- isaacremuant 9y agoAutcomplete is awesome to have. The difference between something like ipython or bpython just for the autocomplete alone is huge, imho.
- gumby 9y agoI am not convinced that immutability matters; this seems like a bias. After all the original REPL, and the name itself, was in Lisp (note that the first Lisp implementations were not interactive, but it was the first interactive language) and Lisp doesn't have immutable data structures. (READ, EVAL, and PRINT are all old Lisp primitives, and the REPL was literally implemented with them. There's also a complex macro called LOOP but it was added much later, and is not integral to the language)
- valw 9y agoAuthor here. I could be wrong of course, but this stems from my experience in using a REPL from JavaScript. I hit 2 sorts of hurdle with mutability there: 1- I wasn't sure what I was viewing in the REPL was what the code of interest was perceiving, could have been changed afterwards and 2- immutable values were more suited for exploratory work due to their speculative nature, e.g I could try a function calls with variants of a data structure without 'committing' to them. Maybe I had a bad REPL experience in JS and blamed it on immutability; OTOH, maybe other people had a great REPL experience in Common Lips for other reasons, and attribute it to mutability. This certainly makes me want to give a new try to Common Lisp :)
- frobozz 9y agoI'd even say immutability can be a disadvantage. I normally only use a repl when I don't know what I'm doing. My normal process with a repl in an immutable language goes like this: 1. Assign something to a name 2. Realise that was wrong, try again. 3. Get told no. Remember to tell the repl to forget the wrong one and do it again (or choose a new name). 4. Try to assign something else to another name (probably 'foo') 5. Realise that I already used foo about an hour ago in a different experiment, and hadn't closed the repl in between. Immutable is great for actual programming. Less so for quick experiments.
- joncampbelldev 9y agoI think there may be some confusion here, immutability is about the stability of values (e.g. I have a string, list, map etc and I want to pass it to others, view its value, make changes without affecting the original value). Immutability is not the same as rebinding a name (in clojure at least) e.g. the following is perfectly valid (global and local binding of the same name repeatedly) (def a 1) (def a 2) (def a 3) (println a) => 3 (let [a 4 a 5 a 6] (println a)) => 6
- msangi 9y agoI like the idea of accessible code. It seems the next level after testable code. Code with a lot of mockable dependencies is usually considered testable but sometimes it's a pain to setup. Accessible code seems to fix this. Instead of taking dependencies, return some data so it's easy to check what your component does in isolation.
- onikolas 9y agoI personally prefer to develop by writing small functions and pairing them with a small test. Hacking in a REPL environment, saving the result and calling it source code has not worked out well for me, I end up producing spaghetti :) I do however find REPL to be invaluable when experimenting/doing research. When you don't even know what the end result is, or when exploring data, you need to iterate over many ideas as quickly as possible and REPL is the fastest way to do that.
- gaius 9y agoSaving state between runs. R has surpassed Python for me just for that feature, for interactive exploration.
- peatmoss 9y agoI've never found a case where I've wanted to retain state between R sessions (I think doing so can be an anti-pattern). But... that image saving feature is very much like that of Common Lisp. Given Ross Ihaka's then-and-now fondness for CL, I'd be shocked if this feature weren't very much intentionally patterned off that. The original implementation of R was on top of a Scheme runtime, but I don't know if images were (then) a feature.
- sedachv 9y agoR workspaces are an idea borrowed from APL: http://aplwiki.com/LearnApl/AplWorkspace http://aplwiki.com/LearnApl/AplWorkspace Common Lisp and Scheme do not have any specifications for persistent state, and the implementations that do have images are all over the place in what those images do and how they are made.
- _pmf_ 9y agoUsing Emacs' Elisp for a short while helped me unlearn some REPL antipatterns I acquired during Clojure development (some of them unfortunately presented in this article), especially this: instead of typing something in the REPL, executing it, then copying it back into your actual source file, you can just write it in your source file and use "evaluate current form" or "evaluate form before current point" to send it to the REPL process for evaluation instead of painstakingly entering it in the REPL and copying it back. Note that this is not a conventional hot-reload, but the form is sent as literal to the REPL (where indirectly, hot-reload may occur).
- igravious 9y ago> Data literals. That is, the values manipulated in the programs have a textual representation which is both readable for humans and executable as code. --8<-- This is not a dig at Kotlin/Java/Whoever but it bugs me (coming from Ruby) no end when regexen don't get to have a regex literal syntax and a match operator. I was going through the Kotlin language docs last night and its such a concise language with well thought out syntax and this omission jumped out at me. Is it me? Do others really not think it's a big deal? I learned how to code via BASIC then ASM then C and early C++ and none of these had regex literals so for the longest time I literally (hah) did not know what I was missing. Now I can't imagine why a language wouldn't have them. I guess Ruby shows its Perl heritage. But Javascript has 'em, you go Javascript, and thus Typescript. And don't get me started on raw strings """Yuck!""" dear Lord, how gruesome. I think how Perl6 is brace savvy is the way forward. I'd also like to be able to specify my own braces to construct my own type as a shorthand, that'd be great DSL, so that, i = %something% would construct an instance of type Foo assuming the correct %T% (by way of example) constructor syntax. That'd be neat-o.
- flavio81 9y agoTake a look at Smalltalk's environment and take a look a t Common Lisp's REPL. They have all the features that make for a good 'REPL'. (As noted before, REPL is a Lisp term that stands for: read - from keyboard input, parse the input string into the syntactic structure of the language eval - eval the expression, this includes binding variables or defining new functions, also re-defining functions, even if such function is currently under execution on the running program. print - print the result of the evaluation (in Lisp all expressions evaluate to something, even if this 'something' is NIL). loop - go to 'read')
- junke 9y ago> in Lisp all expressions evaluate to something Minor nitpick, but note that if you define: (defun foo () (values)) Then (foo) does not return a value and accordingly, the REPL prints nothing. But in a context where you need a value, that value would be NIL: if A evaluates to 3, then after (setf a (foo)) it will evaluate to NIL.
- kazinator 9y agoAccording to a famous epigram by Alan Perlis, Lisp programmers know "the value of everything, but the cost of nothing". It reflects the expectation that a language which calls itself Lisp is expected to produce a value out of any expression which evaluates. Though Common Lisp adds the nuance of multiple values, the behavior you describe is how it conforms to this general expectation. Code written in an everything-really-has-one-value dialect of Lisp can be easily transported to Common Lisp (or at least transported without without difficulties specifically caused by this issue). Scheme, a Lisp-like language, allows some evaluable expressions to have an "undefined" or "unspecified" result value. Logic translated to Scheme from a Lisp dialect without attention to this issue can have a surprising or incorrect behavior. For instance, if the original code executes a do loop, with the expectation that it yields nil (or some similar false/empty value in the original dialect). In Scheme's do loop, if the result expression is present then it specifies the value; otherwise the value is not specified.
- 9y ago
- swah 9y agoI miss developing a function in a REPL and then copy pasting it into the source code. Those Clojure days...
- djKianoosh 9y agoHistory/sessions It's great to be able to see the entire run of your current and past repl sessions.