
What makes a good REPL? - mjmein
http://vvvvalvalval.github.io/posts/what-makes-a-good-repl.html
======
gumby
I 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)

~~~
frobozz
I'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
I 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

~~~
frobozz
I see. When I see "immutable", I think of Prolog and Erlang (e.g. once X is 6,
X can't be matched to 5 in the same scope) rather than languages that simply
prefer byval over byref.

~~~
joncampbelldev
well clojure does pass by reference, but the reference is to something that
cannot be changed (i.e. a persistent data structure)

------
chrissnell
Pretty-printing of data structures in a human-readable format.

~~~
yorwba
I 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.h...](http://ipython.readthedocs.io/en/stable/config/integrating.html?highlight=repr#rich-
display)

~~~
junke
This is the kind of use cases where multiple dispatch is useful.

~~~
yorwba
How so? I feel like single dispatch (as in Python) is already enough.

~~~
junke
If 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.

------
_pmf_
Using 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).

------
agentgt
With 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.

~~~
mjmein
The thing to realise is that, at least is my experience, REPL based
development doesn't mean your are actually typing code into the REPL.

Instead, you develop code in a file, but constantly evaluate code as you go
along.

When I work on a clojure project, I very rarely open the actual REPL, but I am
constantly evaluating code and experimenting with different implementations of
functions.

Then, when I'm happy with the results, I ask the editor to evaluate and insert
the results back into the editor. This then becomes the unit test.

~~~
agentgt
_> The thing to realise is that, at least is my experience, REPL based
development doesn't mean your are actually typing code into the REPL._

 _> Instead, you develop code in a file, but constantly evaluate code as you
go along._

Yes but what you are describing is hot code replacement and evaluation. You do
not need a REPL. For a concrete example Java + JRebel + Debugger (Eclipse
calls it Display with a glasses icon) will do that for you.

In my mind a REPL is very much about the input and of course the output
otherwise its basically what I mentioned above.

And I think the the article doesn't really go into any new innovation or
attempts at making REPLs better (particularly because they mention Bret
Victor)... ie better input and better output.

Yes advanced REPLs have history saving capabilities and what not but then they
are basically competing with the rest of the editor, IDE and source control.

Really innovative REPLs I think are what Bret shows, as well Squeak, and
Racket. Those environments offer really unique input and output.

------
photojosh
"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
I'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.

------
adamnemecek
I 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
Hot 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.

------
bpicolo
I 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
> 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.

------
yoodenvranx
Matlab 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.

~~~
bitwize
Most 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_
Clojure'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.

------
flavio81
Take 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')

~~~
nerdponx
> Common Lisp's REPL

The SBCL and CCL REPLs do not support readline-style editing. This actually
makes them infuriating to use outside of Emacs or some other IDE-like
environment. There is definitely a market for a high-quality, implementation-
independent Lisp REPL.

~~~
SomeHacker44
rlwrap FTW

~~~
nerdponx
Also linedit. But using it still requires you to add it to your favorite
implementation's init file. Would it be possible to write a "meta-REPL" using
linedit? Then again, there already is a Jupyter kernel for CL, so maybe I
should look into making that work for me.

------
igravious
> 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.

~~~
junke
> 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

That's the purpose of readtables in Common Lisp, defining custom readers for
converting external representation to internal ones.

~~~
igravious
Thanks, I'll check it out.

------
onikolas
I 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.

------
breatheoften
Quokka 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
Hydrogen 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/mast...](https://github.com/millejoh/emacs-ipython-
notebook/blob/master/README.rst))

------
msangi
I 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.

------
hamandcheese
There'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
I 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
Though, 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.

~~~
pjmlp
Java 9 is also bringing one.

~~~
taeric
I'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.)

~~~
pjmlp
Well, Beanshell is dead, last update was on 2005.

Then there is a big advantage on having it as standard tool, instead of
something done by third parties.

~~~
taeric
My question in that vein is more of "why will this succeed, where beanshell
failed?"

That is, I had REPL style environments for java a long time ago. And literally
nobody used it. I can see arguments for having the REPL being in actual Java
instead of a shell subset. But, Java has a long way to go from bootstrapping
something in a repl and automatically saving it to something that will work as
a normal entry point. (Though, again, even JRebel has existed for a long time
now.)

------
christophilus
I 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
I 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
You could just store your modified state to new variables though

~~~
junke
In a live-coding session, how does the existing, running code knows that it
should look at the new variables instead of the old ones?

~~~
valw
Have a look at the video. Having immutable values doesn't mean you have
immutable variables / names.

~~~
junke
I know, the parent post was saying "you could just store your modified state
to _new_ variables", not replace what _existing_ variables are bound to.

------
swah
I miss developing a function in a REPL and then copy pasting it into the
source code. Those Clojure days...

------
isaacremuant
Autcomplete is awesome to have. The difference between something like ipython
or bpython just for the autocomplete alone is huge, imho.

------
justifier
Exportable input and output of session

------
djKianoosh
History/sessions

It's great to be able to see the entire run of your current and past repl
sessions.

------
linkmotif
vi mode!

------
whipoodle
I like when you can type part of a line of code, press the up arrow, and have
the REPL complete the line from history.

------
gaius
Saving state between runs. R has surpassed Python for me just for that
feature, for interactive exploration.

~~~
peatmoss
I'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
R 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.

