Settings

Theme

What makes a good REPL?

vvvvalvalval.github.io

168 points by mjmein 8 years ago · 176 comments

Reader

gumby 8 years ago

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)

  • valw 8 years ago

    Author 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 8 years ago

    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 8 years ago

      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 8 years ago

        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 8 years ago

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

        • kqr 8 years ago

          Unification and assignment are very different concepts, though, with only fleeting similarity in some cases.

        • lightbyte 8 years ago

          Mildly off-topic: automatically shadowing bindings is one of my favorite changes elixir made to erlang

      • junke 8 years ago

        Some languages have immutable bindings too, where you only shadow previous bindings.

        • joncampbelldev 8 years ago

          If you can shadow previous bindings then that would also cause no problem for repl re-usage, unless the shadowing syntax is different to the binding syntax.

          Either way, unrelated to the point in the article about the advantage of immutable values for repl, not immutable bindings.

          I would certainly agree that truly immutable bindings would be at best quite a stumbling block for repls, but so far no one was advocating for them as a good repl feature.

          • dllthomas 8 years ago

            > so far no one was advocating for them as a good repl feature

            GHCi has this feature, if you explicitly turn it on (-fwarn-name-shadowning with -Werror). I agree that it's not a terribly useful way to run a REPL.

      • kazinator 8 years ago

        For a global def to be a "rebinding" rather than assignment is crippling. It means that previous references to a still see the old thing, which is wrong if you want them to see the new thing.

        • kqr 8 years ago

          If you want others to see the new thing automatically, you need something much more powerful than plain old assignment anyway: something which will let you pick a synchronization strategy.

          "Changes at any arbitrary time, including when you are halfway through reading it" is not in any way a sound synchronization strategy and the only thing you get with assignment.

          You seem to understand the important difference between identity (what you see when you read an object) and reference (what you use to access the object). Next important thing on the list is how "reference" cannot just be a pointer to a place in memory -- unless it is immutable.

          • kazinator 8 years ago

            > you need something much more powerful than plain old assignment anyway: something which will let you pick a synchronization strategy.

            Right, just like the king's subjects don't actually need a toaster, but rather breakfast food cooker.

        • didibus 8 years ago

          Clojure does not rebind the reference, it just changes it to point to something else. So it does not suffer from what you describe. Previous references will see the new thing as you'd hope.

          So def on an existing binding does assignment. That's what is meant by "re-binding".

      • valw 8 years ago

        Indeed, by immutability I really meant immutability of the values, but not of the name bindings. So I would say an optimal setup is to have a mutable execution environment which handles immutable values.

  • baldfat 8 years ago

    > Lisp doesn't have immutable data structures

    Racket does have immutable data structures and Dr. Racket has a good REPL IMHO.

    http://beautifulracket.com/explainer/data-structures.html

    • gumby 8 years ago

      That's the converse of my point. The article claimed immutable data is important for a good REPL and I said that the claim was an overstatement. I didn't say that immutable data makes a REPL impossible.

      I quite like immutable datastructures, as it happens.

      • baldfat 8 years ago

        I was replying that LISP doesn't have immutable data structures.

        • kazinator 8 years ago

          Common Lisp has some immutable data structure such as, oh, numbers. If you arithmetically encode a record of data into a bignum integer, that is immutable.

chrissnell 8 years ago

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

  • yorwba 8 years ago

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

    • junke 8 years ago

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

      • yorwba 8 years ago

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

        • junke 8 years ago

          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_ 8 years ago

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 8 years ago

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.

  • mjmeinOP 8 years ago

    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 8 years ago

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

    • dagw 8 years ago

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

      Half the time I find I go the other way. I develop in jupiter, and copy the function to a file once I get it working.

  • tedmiston 8 years ago

    One nice thing about the Python REPL IPython is the ability to turn the ephemeral session into a file which contains every command in the session like a history file by just typing %logstart. Most of my projects start this way.

    • lispm 8 years ago

      Lisp has the function DRIBBLE for that.

      SLIME provides slime-repl-save-history ...

  • pjmlp 8 years ago

    REPLs 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 8 years 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.

      • pjmlp 8 years ago

        > You'll have to patiently elaborate more for me.

        They are image based, so you can just save your session and continue using it later in another day.

        > I think the author alluded to that. Of course I have no experience with Mesa/Cedar Oberon. I'll have to check those out.

        On those systems, the unit of loaded code is a module and the whole OS only has dynamic libraries as executables.

        So you can just reload a module and the next time you do module.proc on the repl, you will be referring to the newly loaded module.

        For me an ideal REPL should be like the experience I used to have in Smalltalk.

        • agentgt 8 years ago

          Yes I agree on the Smalltalk point. I think Squeak was at one point the very future of REPL-like development and hence why I think the article doesn't really go into how REPLs could be better and how they aren't really that much better than other development tools (ie IDE + debugger,... I mention this in other comments).

          I had no idea that common lisp had image saving! I only used the Carnegie Mellon one in college.... its been a long time.

          • lispm 8 years ago

            > I had no idea that common lisp had image saving!

            Lisp has image saving since around 1960...

            • agentgt 8 years ago

              I probably did at one point know this as I did use it in college but for some reason forgot it (we are talking 15 plus years) given that is apparently how you distribute executable lisp code... IIRC though I hated image saving when it came to Squeak aka Smalltalk so I'm not sure if I did like that.

              • lispm 8 years ago

                > given that is apparently how you distribute executable lisp code

                LispWorks can:

                * save images

                * create optimized images/applications for delivery, using a treeshaker for removing unused stuff

                * can generate Mac applications with the usual ceremony/ application bundles

                * can generate shared libraries which can be linked into programs written in C or similar

                Some other compilers can generate standalone C code doing whole-program compilation. For example mocl or some inhouse compilers used by companies.

  • RyanHamilton 8 years ago

    I also used junit as a semi-REPL similar to how you describe. then I started playing with a REPL for a fully dynaimic language and when I came back to java, there were parts that I missed.

    So I actually created a java REPL: http://jpad.io

    I would say the things it does that the typical junit run does not are:

    1. Explorative queries, send sql statements see the results quickly. What particularly helps here is that any collection is converted to a table with each column representing a getXXX method.

    2. Command line instant queries. Sometimes I just want an advanced calculator on the command line. I do "jpad -e 2+2" and it returns 4. No messing around with IDEs.

    3. Automatic smart guessing of imports and ability to upload results straight to website to share with colleagues.

    That's what I liked, so I built it in. The lack of traffic may suggest others did not find it as useful :)

  • abhirag 8 years ago

    REPLs don't necessarily have to be ephemral, this is my REPL session(Jupyter Notebook) exported as HTML -- (http://abhirag.in/articles/train_of_thought_1.html), and also great REPLs can help in interactive static analysis, as a case in point consider the Idris REPL (http://docs.idris-lang.org/en/latest/reference/repl.html).

    • agentgt 8 years ago

      Yes I think you make an excellent point but more so because the output of the REPL session is much better than a typical REPL session of just plain text.

      Sort of in a round about way but in my original comment my hope was to elicit the discussion that modern development tools of ide visualization + debugger + hot code swapping are not far off from traditional REPLs and in same cases better because the inputs and outputs are better.

      That is traditional REPLs (ie commandline with maybe some readline capabilities) I think aren't that much better the inputs/outputs aren't that good.

      To your point on the static analysis I agree but a truly interactive development system that allows google-esque querying I think is far more than a REPL or at least the REPLs I know/knew of but I guess REPL definition can be somewhat nebulous these days.

      • abhirag 8 years ago

        The interactive development workflow in Idris is best described as type driven development and the focus seems to be on making the REPL an interface where an intelligent dialog can happen in between the programmer and the compiler, with the compiler trying to nudge you in the right direction. It is kinda fun, you should definitely check it out[1].

        And regarding your point about the modern IDE workflow, I personally think that the sweet spot is somewhere in between, I personally prefer having a REPL process in the background I can interact with while still writing code in my source file and a REPL kept open as a scratch space where I can experiment freely without messing up my source code, I feel much better experimenting in a REPL, but to each his own I suppose :)

        Jupyter Notebook is a completely different beast though, modern REPL + literate programming, what's not to love.

        [1] -- (https://www.manning.com/books/type-driven-development-with-i...)

        • agentgt 8 years ago

          I have been meaning to look at Idris for so long! Thanks for the inspiration to move that higher up the queue :)

          • abhirag 8 years ago

            Definitely start with the book I mentioned, it is by Edwin Brady, he leads the development of Idris. I am reading it right now and it is an enjoyable read :)

  • valw 8 years ago

    Well, what are your thoughts on the 'Write fewer tests, faster' section of the article?

    • agentgt 8 years ago

      I assume you are the author? I think you make valid case/points but I have some kind counter points if you don't mind

      > 1. Having too many unit tests makes your codebase harder to evolve. You ideally want to have as few tests as possible capture as many properties of your domain as possible.

      Yes but I have found what often happened for me with with REPL environments is the actual code base would be littered with stuff to massage the REPL (commented out or left behind). At least with the unit tests that playing around stuff is away from the actual code.

      For both cases there is always the delete button :) . Also for some reason many developers I have worked with don't seem to have a problem deleting or putting an ignore on a test. After all the tests are source controlled. I do get your point but I don't think its that strong.

      > 2. Tests can only ever answer close-ended questions: "does this work?", but not "how does this work?", "what does this look like?" etc.

      I fail to understand this point. I mean you can obviously write tests that just run stuff and not throw an exception or error. Furthermore you can share how you set stuff up with other developers.... and again you can just delete it if its obnoxious.

      > 3. Tests typically won't run in real-world conditions: they'll use simple, artificial data and mocks of services such as databases or API clients. As a result, they don't typically help you understand a problem that only happens on real-life data, nor do they give you confidence that the real-life implementations of the services they emulate do work.

      This is exactly what I do not like about REPLs. You setup a custom environment and its hard to keep track of what you have done. I don't like the "not repeatable" nature of it. I do think you make excellent points about how immutability helps that problem as stuff basically becomes a log but for other languages this is not the case.

      However this is by far your strongest point. There are languages that allow you to play with a system while its running. Perhaps not through the command line but through a debugger. The Java debugger in Eclipse/IntelliJ can evaluate expressions and are not far off from being REPLs.... in some cases the debuggers are stronger than REPLs.

      • valw 8 years ago

        Yes that's me :)

        >> 2. Tests can only ever answer close-ended questions: "does this work?", but not "how does this work?", "what does this look like?" etc.

        > I fail to understand this point. I mean you can obviously write tests that just run stuff and not throw an exception or error. Furthermore you can share how you set stuff up with other developers.... and again you can just delete it if its obnoxious.

        Yes, but the point is that a test mostly gives you a Yes/No answer, not a visualization. What's more, sometimes you need to set up a fair amount of state as you explore (think of the examples in the video, where you call an external API based on a previous result of that external API etc.) - not something that is convenient to do in a test.

        > > 3. Tests typically won't run in real-world conditions: they'll use simple, artificial data and mocks of services such as databases or API clients. As a result, they don't typically help you understand a problem that only happens on real-life data, nor do they give you confidence that the real-life implementations of the services they emulate do work.

        > This is exactly what I do not like about REPLs. You setup a custom environment and its hard to keep track of what you have done. I don't like the "not repeatable" nature of it. I do think you make excellent points about how immutability helps that problem as stuff basically becomes a log but for other languages this is not the case.

        It's not that the environment is custom, is that it's real. Do you call your payment service or your mail sending service from your tests? This is exactly the kind of thing you want to experiment with in a supervised, non-repeatable way.

        You can put stuff in the REPL, or in your test suite, or both, and there are pitfalls in each case, but at least you have a choice; indeed, you have to use your best judgement to decide what will be persisted, repeated and shared with your team and what will be forgotten after your REPL session end, but I wouldn't call making the wrong choice a deficiency of the REPL, rather an error on the programmer's side.

        • agentgt 8 years ago

          > It's not that the environment is custom, is that it's real. Do you call your payment service or your mail sending service from your tests? This is exactly the kind of thing you want to experiment with in a supervised, non-repeatable way.

          The irony you mentioning that is I have actually written "tests" that call braintree and stripe...

          Just to clarify when I say "test" I don't mean some precise definition of "unit test" or "integration test". I mean in a runner that runs your code. Unit tests basically allow you to make a whole bunch of entry points (aka static main(args)).

          I have many times setup a particularly environment and then repeatedly ran a unit test against that environment (I then commented or deleted that code later).

          I think in large part of what your saying is good about REPLs is that they allow hot code swapping but that is only IMO really one part of the REPL. The key to really good REPL should be human input (think Excel) and human output (think images and graphs). There was a company recently shown on HN called Luna [1] and I think that is what a REPL should be.

          And I particularly pick on this because you mention Bret Victor who is (err I guess was) actually working on environments like this.

          Otherwise call it a unit test... call it a REPL... call it hot code swapping but in current JVM environments the difference can be pretty nebulous.

          [1]: https://news.ycombinator.com/item?id=14612680

          • valw 8 years ago

            > I think in large part of what your saying is good about REPLs is that they allow hot code swapping but that is only IMO really one part of the REPL. The key to really good REPL should be human input (think Excel) and human output (think images and graphs).

            I definitely could have spent more time on this part in the article. You may want to have a look at Proto-REPL (https://atom.io/packages/proto-repl), which is one of Clojure's REPLs (for the Atom editor)

  • tikkabhuna 8 years ago

    I'm interested to see how useful Java's REPL is going to be. Like you I will use a JUnit test, or maybe a standalone class with a main method, to test a small bit of code. With Eclipse's debug support I don't feel the need for a REPL.

    • _pmf_ 8 years ago

      > With Eclipse's debug support I don't feel the need for a REPL.

      Clojure managed to make the tremendously powerful JVM debugging ecosystem completely useless (without providing any replacement with equal power).

      • joncampbelldev 8 years ago

        As a person who has used the standard java debugger in intellij for clojure, could you expand on what features are missing?

        I find I don't get much use out of it anyway, due to immutable values and (mostly) pure functions I find I don't usually need the whole program to be running and frozen in time.

        However, I'd like to know what I'm missing.

  • Insanity 8 years ago

    Java is getting a REPL though. I believe it is part of Java 9 ;).

    • agentgt 8 years ago

      However to the author's point I believe some languages are inherently superior for REPLs particularly if they have powerful literal syntax.

      Java is not a fun language to type expressions in and IMO neither is Python albeit for completely different reason. I can elaborate more if you like but I think most will agree.. Java will be a pain to type in a REPL.

      • abhirag 8 years ago

        You sir, seem to be hell-bent against REPLs :P

        I agree with your point that IDEs are getting better but REPLs are getting better too, why should they be relegated to be mere artifacts of the past. Your java REPL will mostly have autocomplete, give it a chance, it really might turnout to be fun :P

        And typing python in a REPL is fun for me, but fun is subjective, no point arguing about it :)

        • agentgt 8 years ago

          I'm not against REPLs I just think they have been around since early Lisp and there are vast improvements that can be made.

          I use REPLs all the time... Bash is basically a REPL :)

          The reason I think Java REPL would be awful is not because I dislike REPLs but because Java is really painful for that kind of mutable command line like development. Like just making a damn struct like object is absolutely painful and Java does not have structural types (ignoring FunctionalInterfaces which isn't really structure types).

          Python REPL is painful because of required indentation and again because Python is similar to Java and prefers nominal types.

          I'm not against Python or Java but I don't think the language design of those languages really works well for REPL compared to say Lisp, OCaml, Haskell, or even Scala and Smalltalk.

          • abhirag 8 years ago

            Fair enough, I think I am having difficulty in understanding your views because firstly I have never worked with Java, closest language I have worked with is C# and I don't particularly dislike its REPL, its nothing to write home about but I don't particularly hate it either.

            Secondly the image we two have in mind of a REPL seems to be different. When I am thinking about a REPL the image I have in mind is of Jupyter Notebook and Clojure, Elixir, Idris, Haskell REPLs in Emacs. The image you have in mind seems to be a basic console, so having spent my day working in a Jupyter Notebook I sit here thinking what you mean by Python's significant whitespace being an issue(Haskell and Idris have significant whitespace too). But now I do understand your views :)

            P.S. I didn't get your point about python having nominal types, duck typing seems closer to structural typing to me and mypy seems to support both, but maybe I misunderstood you. Thanks for the thought provoking discussion though :)

            • agentgt 8 years ago

              > When I am thinking about a REPL the image I have in mind is of Jupyter Notebook and Clojure, Elixir, Idris, Haskell REPLs in Emacs

              Thats my point is that the REPL case of using Emacs to run and evaluate your code is almost hardly different than letting an IDE run your unit test. With Java this evident because the IDE compiles incrementally and the debugger can hot code swap.

              The power of the REPL should not be the evaluation portion but the input or the print otherwise you can just about do any quick evaluation for any language that compiles reasonable fast.

              Other than Jupyter Notebook the ones you mention don't really have any amazing output other than pretty print. To the authors point it also helps for the pretty print if the language is homoiconic.

              As for input there is even fewer that have Excel like rapid response feedback. See Bret Victor on this. There was a recent company presented here on HN called Luna [1] who have a very cool REPL. Now that is where I think REPLs should be.

              > P.S. I didn't get your point about python having nominal types, duck typing seems closer to structural typing to me and mypy seems to support both, but maybe I misunderstood you. Thanks for the thought provoking discussion though :)

              For the most part you need to name functions in Python (lambda support I believe is even on the way out but I can't recall the status). In fact other than I guess tuples you need to name everything in Python.

              But to your point structural typing means less in a duck typing environment particularly one with really late dispatch.

              [1]: https://news.ycombinator.com/item?id=14612680

              • lispm 8 years ago

                > Thats my point is that the REPL case of using Emacs to run and evaluate your code is almost hardly different than letting an IDE run your unit test. With Java this evident because the IDE compiles incrementally and the debugger can hot code swap.

                Using a REPL vs. an IDE like you describe is the difference between a conversation and sending somebody a letter with instructions.

                This is more visible when we use more dynamic languages/runtimes than Java/JVM. Since the changes one can do and how they need to be done is not very advanced, the usefulness of a REPL is reduced.

                • agentgt 8 years ago

                  I have a feeling given your screen name I'm talking to some who is equally biased as I am to Java :)

                  > Using a REPL vs. an IDE like you describe is the difference between a conversation and sending somebody a letter with instructions.

                  Hmmm an IDE is supposed to be a REPL and more. I mean you can go look up the definition from wikipedia.

                  > This is more visible when we use more dynamic languages/runtimes than Java/JVM. Since the changes one can do and how they need to be done is not very advanced, the usefulness of a REPL is reduced.

                  Yes I completely agree as I mentioned dynamic languages are far easier to modify at runtime. However for the case with Java it can be done with JRebel and various other tools.

                  Furthermore going back to the whole conversation vs letter an IDE with a powerful debugger will let you evaluate expressions based on a state that is stuck... ie setting breakpoint (as well of course as investigating current variables and such). This is damn useful for dealing with a multithreaded environment.

                  By the way make no mistake... I do love Lisp... I just think there are better things than traditional REPLs considering to your other point in another thread this stuff has existed since the 70s.

                  • lispm 8 years ago

                    > I have a feeling given your screen name I'm talking to some who is equally biased as I am to Java :)

                    The main difference: I have a Lisp Machine at home. :-)

                    > Hmmm an IDE is supposed to be a REPL and more.

                    No, a Read Eval Print Loop came from Lisp in the early 60s. It originally means to read a data structure, treat it as code and evaluate it and print the result data structure. READ, EVAL, PRINT are actual functions in Lisp. This stuff executes in a LOOP and is enriched by all kinds of stuff.

                    An IDE does not need to have a REPL. If it can interact with a running application (for example via a debugger), this might still not be a REPL.

                    > However for the case with Java it can be done with JRebel and various other tools.

                    Even JRebel can not do to a running JVM application what some Lisp implementations can do. Not near of that.

                    > IDE with a powerful debugger will let you evaluate expressions based on a state that is stuck... ie setting breakpoint (as well of course as investigating current variables and such)

                    This is pretty basic.

                    > traditional REPLs

                    Check out Symbolics Dynamic Windows and McCLIM on the Lisp side...

                    Old demos from me:

                    https://www.youtube.com/watch?v=VU_ELJjbnWM

                    https://www.youtube.com/watch?v=9whxPd4haKc

                    http://lispm.de/videos/lispm-3a.mov

                    • agentgt 8 years ago

                      I think I'm in agreement with you. What I meant by the IDE is that "ideally" it should have REPL like offerings if the language can support hot code swapping.

                      I still don't think its REPL that makes Lisp or clojure magic (when I say magic I mean awesome). Its all the other stuff like macros and homoiconicity (which I see your point plays some part in academic REPL).

                      > Even JRebel can not do to a running JVM application what some Lisp implementations can do. Not near of that.

                      Well thats because of the Java compiler and in some parts the language of Java. It has nothing to do with the JVM otherwise Clojure wouldn't work. But I agree JRebel is far cry from the full reloading capabilities of Lisp, Erlang and other dynamic languages.

                      > IDE with a powerful debugger will let you evaluate expressions based on a state that is stuck... ie setting breakpoint (as well of course as investigating current variables and such)

                      > This is pretty basic

                      I agree but its still surprising how many languages do not do this well and I didn't mention that you can execute simple expressions in that mode something other static languages like C will not allow.

                      Besides.... I can change a function name in Java or Scala and see immediately everywhere in my code base with (e.g. red squiggle lines) how that impacts other code... for static languages that is pretty basic :P

                      I'm totally envious of your lisp machine (EDIT: in all honesty...I realize that originally sounded sarcastic).

                      • lispm 8 years ago

                        > Well thats because of the Java compiler and in some parts the language of Java. It has nothing to do with the JVM otherwise Clojure wouldn't work.

                        Clojure is constrained by the JVM and its implementation. Though Java is even more constrained.

                        You get a mini Common Lisp Object System demo in the LispWorks REPL:

                        We define a class person with a slot 'name':

                          CL-USER 1 > (defclass person () ((name :initarg :name :accessor name)))
                          #<STANDARD-CLASS PERSON 402005BA03>
                        
                        Let's create a list of persons:

                          CL-USER 2 > (setf persons (mapcar (lambda (name)
                                                              (make-instance 'person :name name))
                                                            '("Jan" "Ralph" "Joan")))
                          (#<PERSON 40200A493B> #<PERSON 40200A49F3> #<PERSON 40200A4AAB>)
                        
                        Let's define a custom print method:

                          CL-USER 3 > (defmethod print-object ((p person) stream)
                                        (print-unreadable-object (p stream :type t :identity t)
                                          (write-string (name p) stream)))
                          #<STANDARD-METHOD PRINT-OBJECT NIL (PERSON T) 40200A942B>
                        
                        
                        How does a person print now?

                          CL-USER 4 > persons
                          (#<PERSON Jan 40200A493B> #<PERSON Ralph 40200A49F3> #<PERSON Joan 40200A4AAB>)
                        
                        
                        Let's add a slot to the class, a slot 'age':

                          CL-USER 5 > (defclass person ()
                                        ((name :initarg :name :accessor name)
                                         (age  :initarg :age  :accessor age :initform 0)))
                          #<STANDARD-CLASS PERSON 41404737D3>
                        
                        Let's update the print method:

                          CL-USER 6 > (defmethod print-object ((p person) stream)
                                        (print-unreadable-object (p stream :type t :identity t)
                                          (format stream "~a ~a" (name p) (age p))))
                          #<STANDARD-METHOD PRINT-OBJECT NIL (PERSON T) 40201289FB>
                        
                        Woops: all persons now have already got the new slot:

                          CL-USER 7 > persons
                          (#<PERSON Jan 0 4140473733> #<PERSON Ralph 0 4140473933> #<PERSON Joan 0 4140473D3B>)
                        
                        Let's set the new slot:

                          CL-USER 8 > (mapc (lambda (p age)
                                              (setf (age p) age))
                                            persons
                                            '(23 43 21))
                          (#<PERSON Jan 23 4140473733> #<PERSON Ralph 43 4140473933> #<PERSON Joan 21 4140473D3B>)
                        
                        
                        Let's define a new class: social-security-mixin:

                          CL-USER 9 > (defclass social-security-mixin ()
                                        ((social-security-number :initarg :ssn :accessor ssn)))
                          #<STANDARD-CLASS SOCIAL-SECURITY-MIXIN 40200111C3>
                        
                        
                        Let's add this new class to the superclasses of PERSON.

                          CL-USER 10 > (defclass person (social-security-mixin)
                                        ((name :initarg :name :accessor name)
                                         (age  :initarg :age  :accessor age :initform 0)))
                          #<STANDARD-CLASS PERSON 41404737D3>
                        
                        Now we do something really wild: we write an around method for printing:

                          CL-USER 11 > (defmethod print-object :around ((p person) stream)
                                         (print-unreadable-object (p stream :type t :identity t)
                                           (call-next-method)))
                          #<STANDARD-METHOD PRINT-OBJECT (:AROUND) (PERSON T) 40200AB8A3>
                        
                        We redefine the original method just to print the name and age of the person.

                          CL-USER 12 > (defmethod print-object ((p person) stream)
                                         (format stream "~a ~a" (name p) (age p)))
                          #<STANDARD-METHOD PRINT-OBJECT NIL (PERSON T) 40200AC9EB>
                        
                        Then we define an AFTER method for the social-security-mixin class:

                          CL-USER 13 > (defmethod print-object :after ((o social-security-mixin) stream)
                                         (format stream " ~a" (ssn o)))
                          #<STANDARD-METHOD PRINT-OBJECT (:AFTER) (SOCIAL-SECURITY-MIXIN T) 402001917B>
                        
                        Now we set the social security number of the persons. Wait? Lisp has updated my objects, since I added a new superclass to their class? All objects now have a changed superclass for their class? They inherit the new slot?

                        And the print-method gets reassembled for the new inheritance tree and the changed set of methods?

                          CL-USER 14 > (mapc (lambda (p ssn)
                                              (setf (ssn p) ssn))
                                            persons
                                            '("123-345" "321-455" "443-222"))
                          (#<PERSON Jan 23 123-345 4140473733> #<PERSON Ralph 43 321-455 4140473933> #<PERSON Joan 21 443-222 4140473D3B>)
                        
                        As you see the objects have a SSN and the print methods are dynamically combined. For the person it runs the around method, then the primary method of person and then the after method of the mixin. If I'd now change the inheritance tree, then the methods would be recombined according to the inheritance at runtime... I could also dispatch on the second argument...

                        CLOS supports multi-dispatch over multiple-inheritance with dynamic combinations of applicable methods.

                        CLOS can do quite a bit more than that...

                        Java can't do anything like that.

                        It can't update objects on class changes/inheritance changes/...

                        It can't combine methods based on the multiple-inheritance class tree.

                        It can't change the class of objects. It can't reprogram the object system itself. See the CLOS MOP...

                        • agentgt 8 years ago

                          Yes CLOS is superior particularly multimethods.

                          As for the JVM: https://common-lisp.net/project/armedbear/

                          Sooo its not the JVM.

                          BTW AspectJ and JRebel will get you around methods and even inheritance changes but alas Java does not have multimethods or MOP. I mean CLOS is awesome but so is static analysis :)

                          • lispm 8 years ago

                            ABCL implements CLOS classes in Java. It does not use the Java/JVM directly. For example a CLOS class is an instance of some Java class. This instance then has an attribute which has a vector of the CLOS slots. CLOS slots are not Java attributes themselves... The JVM object model is simply not able to provide CLOS features directly.

                            Last I've looked Jrebel used a funny mechanism. One couldn't just tell the class to add a slot, but one has to have Jrebel installed and given a new class file, it will detect it and then change/load the class...

                            That's a rather limited mechanism aimed at development... especially since it needs a license to work...

          • stevedonovan 8 years ago

            I have to agree about Java, but hundreds of thousands of people seem to be happy using Python REPL, even if just to discover an API. At least it _does_ have a literal syntax for things like maps.

            • agentgt 8 years ago

              Yeah the Python one was a stretch and I often use the Python REPL as a calculator (I have no real strong idea why but I guess because numpy is what I know).

photojosh 8 years 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 8 years ago

    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.

  • ben-schaaf 8 years ago

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

adamnemecek 8 years ago

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.

  • aidos 8 years ago

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

  • seanmcdirmid 8 years ago

    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 8 years ago

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 8 years 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.

yoodenvranx 8 years ago

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.

  • suraj 8 years ago

    Jupyter[1] lifted this feature from Matlab & made it work with any kernel.

    [1]: http://jupyter-notebook.readthedocs.io/en/latest/examples/No...

  • bitwize 8 years ago

    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_ 8 years ago

      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.

    • jasonm23 8 years ago

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

  • abhirag 8 years ago

    You seem to be using SublimeText, check out SublimeREPL extension[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.

  • blt 8 years ago

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

  • mclehman 8 years ago

    Lisp + Emacs + SLIME: miss no longer.

    Picking them all up is a tall order though.

    • pjmlp 8 years ago

      Emacs 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

      • abhirag 8 years ago

        I 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/mast...) [2] -- (https://python-pillow.org/)

        • pjmlp 8 years ago

          That 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 8 years ago

            The 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/debug...)

          • bitwize 8 years ago

            SLIME doesn't give you this?

            • pjmlp 8 years ago

              I don't know, back when I cared about Lisp development on Emacs, SLIME wasn't a thing, as you might understand from my XEmacs reference.

              • juki 8 years ago

                SLIME does let you redefine functions and restart a stack frame (assuming the Lisp implementation supports that). You can even do it without using Emacs/SLIME, although that's not very convenient of course. A silly example with SBCL,

                    ~ $ rlwrap sbcl --noinform
                    CL-USER> (defun foobar (x y)
                               (if (evenp x)
                                   (/ x y)
                                   (* x y)))
                    
                    FOOBAR
                    CL-USER> (foobar 10 0)
                    
                    debugger invoked on a DIVISION-BY-ZERO in thread
                    #<THREAD "main thread" RUNNING {1001BB64C3}>:
                      arithmetic error DIVISION-BY-ZERO signalled
                    Operation was /, operands (10 0).
                    
                    Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
                    
                    restarts (invokable by number or by possibly-abbreviated name):
                      0: [ABORT] Exit debugger, returning to top level.
                    
                    (SB-KERNEL::INTEGER-/-INTEGER 10 0)
                    0] backtrace 3
                    
                    Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {1001BB64C3}>
                    0: (SB-KERNEL::INTEGER-/-INTEGER 10 0)
                    1: (FOOBAR 10 0)
                    2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (FOOBAR 10 0) #<NULL-LEXENV>)
                    
                    0] down
                    (FOOBAR 10 0)
                    
                    1] source 1
                    
                    (IF (EVENP X)
                        (#:***HERE*** (/ X Y))
                        (* X Y)) 
                    1] (defun foobar (x y)
                         (if (and (evenp x) (not (zerop y)))
                             (/ x y)
                             (* x y)))
                    
                    WARNING: redefining COMMON-LISP-USER::FOOBAR in DEFUN
                    FOOBAR
                    1] restart-frame
                    
                    0
    • taeric 8 years ago

      What do you mean with "picking them all up?"

      • _mhr_ 8 years ago

        They mean the learning curve is steep.

        • pklausler 8 years ago

          A "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 8 years ago

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

        • taeric 8 years ago

          Ah, 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.

  • jhgb 8 years ago

    Lisp buffers? Smalltalk workspaces? Is this capability any different from those?

  • auggierose 8 years ago

    I guess Mathematica has the most advanced interface in that respect. That's what inspired all the other repls like Jupyter.

flavio81 8 years ago

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

  • junke 8 years 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 8 years ago

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

      • flavio81 8 years ago

        > Scheme, a Lisp-like language, allows some evaluable expressions to have an "undefined" or "unspecified" result value.

        That's why they are heretics !!

        (Just joking of course... Scheme is the other great Lisp dialect.)

  • nerdponx 8 years ago

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

    • flavio81 8 years ago

      > The SBCL and CCL REPLs do not support readline-style editing. This actually makes them infuriating to use outside of Emacs

      TL;DR: I fail to see how this can be a problem.

      Yes, but they do work fine inside Emacs (or perhaps inside other IDEs), and if i was using the SBCL command-line and needed readline-style-editing, then it was because I'm developing or debugging, so I'd be inside Emacs (or other IDE) in the first place...

    • SomeHacker44 8 years ago

      rlwrap FTW

      • nerdponx 8 years ago

        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 8 years 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.
  • junke 8 years ago

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

onikolas 8 years ago

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 8 years ago

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

msangi 8 years ago

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 8 years ago

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.

  • tikhonj 8 years ago

    I don't think there's anything fundamental about a compiled language that prevents it from having a REPL. (If anything, you could always have a dedicated interpreter for interactive work—"compiled" and "interpreted" are functions of the implementation, no the language.)

    In practice the compiled languages I've used extensively (OCaml and Haskell) do have REPLs, but ones that aren't nearly as powerful as some other languages. I'm not sure exactly why it's the case, but I certainly don't think it's impossible for them to have good REPLs. My guess is that there are some properties of the languages that make a good REPL a bit more difficult to implement, and there simply hasn't been enough community investment to overcome that.

    I wish there was because I basically live in GHCi (Haskell's REPL) and sorely wish for a few core improvements like hot loading updated code when possible.

    • Zak 8 years ago

      > If anything, you could always have a dedicated interpreter for interactive work

      CMUCL does that. There's an interpreter that's used for the REPL and optionally for loading files on the fly, and an optimizing AOT compiler.

      SBCL drops the interpreter and just runs the compiler with settings that make it reasonably fast for interactive use as I recall. Clojure, too just uses the compiler interactively and not a separate interpreter.

      • mamcx 8 years ago

        I wonder how do that. I'm building a language that truly will benefit for a REPL (is for database development) but also do it compiled simplified other things.

        Is done on .NET/F#. I wonder how architect the thing so I can have a good repl yet compiled... but how?

        • Zak 8 years ago

          Since more than a few language implementations already do things like that, I'm inclined to say that's more or less a solved problem.

          • mamcx 8 years ago

            Yeah, but I have not find a resource that explain how is done.

            • Zak 8 years ago

              I think is probably an RTFS sort of topic, as there may not be a manual.

              • mamcx 8 years ago

                RTFS? What this mean?

                Is like "some inside knowledge" but undocumented?

        • bernadus_edwin 8 years ago

          I hope jetbrain will do something magic on repl

  • gfody 8 years ago

    I think the compiled language analog of a REPL is something like LINQPad for C# where you code in the top pane, highlight a snippet to execute, and get the results in the bottom pane. Combined with the more functional-style language features it's very conducive to layering on complexity and analyzing code where you can execute inner layers to see what they do.

  • mhluongo 8 years ago

    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

    • dllthomas 8 years ago

      Haskell has one - called GHCi. By these criteria:

      1) You can define new functions (and values, and types, type classes, instances, etc). You can "redefine" these things only insofar as you can shadow them.

      2) I'm not sure whether they mean the ability to persist your state to disk and restore it (which GHCi lacks), the ability to refer to previous results (in GHCi, the previous result is called `it`), or just the ability to bind variables (of course you can do this in GHCi).

      3) Usually "Show" instances are meant to be embeddable in code. Sometimes they need a little massaging. Sometimes they're just broken, from this POV. Sometimes they're just broken, period. But it holds for a lot of values.

      4) You can run GHCi in the context of your project (see cabal repl and stack repl commands).

      5) GHCi very much fails at this - no way to add anything to a module, so far as I'm aware.

      6) GHCi more-or-less lacks this kind of functionality. You could run your server's "main" function from the REPL, but there's not much you can do to it.

      7) :reload

      8) There's an increasing amount of such tooling; only some of it has any particular tie to the REPL, per se.

      • kccqzy 8 years ago

        Yes for (3)! I just plainly hate data types whose Show instances aren't just Haskell syntax. They usually try to add some pretty printing but that just ends such hurting copy-pasting from the REPL to the editor.

    • taeric 8 years ago

      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.

      • mhluongo 8 years ago

        Some 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 8 years ago

        Java 9 is also bringing one.

        • taeric 8 years ago

          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 8 years ago

            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 8 years ago

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

    • ced 8 years ago

      Julia has had a good REPL since its inception, (along with the awesome Jupyter notebooks). It also allows live code replacement (eg. Revise.jl).

    • markc 8 years ago

      The language used as an example in the article (and one that fulfills its criteria for a good REPL) is Clojure - which is compiled.

    • slaymaker1907 8 years ago

      Java is getting one with the upcoming Java 9 release.

      • vorg 8 years ago

        I doubt many programmers already using Jython, Beanshell, Apache Groovy, Xtend, Rhino, or Nashorn will change to the Java 9 JShell. I switched from Groovy to Clojure for scripty stuff a few years ago, mainly for the macros. A well-placed macro can avoid a lot of clutter in repetitive testing -- a far better solution than some heavy-weight testing framework.

    • rockostrich 8 years ago

      Scala's REPL is fine. It has a useful paste mode as well.

    • abhirag 8 years ago

      C# and F# do too.

    • alphaalpha101 8 years ago

      They aren't really REPLs. A REPL isn't just a prompt that you can put code into.

      • rockostrich 8 years ago

        REPL stands for read-eval-print loop. So yes, it is just a prompt that you can put code into that will print a result.

        • alphaalpha101 8 years ago

          If you want to be literal about it then sure, it just means read-eval-print loop. But I think that's akin to saying that a functional programming language is a language that has functions in it.

          EDIT: To be clear, what I'm saying is that when people say 'I really love using Common Lisp because it has a REPL' they aren't saying 'I really love using Common Lisp because it has a prompt I can write raw strings of code into that executes that code and has no other features'. That's not a lovable feature.

          People love Lisp REPLs because there's much more to them than that. In Lisp, the REPL is more like GDB than it is like Python's REPL.

          Not really sure why the reaction to my comments here is so viscerally negative. Very few terms that we use are wholly literal. REPL isn't literal either.

          • rockostrich 8 years ago

            Sorry if my comment with the definition of REPL seemed "viscerally negative". I understand that a good REPL has more features that just the bare bones, but you said that programs that read an input, evaluated it, and then printed the result aren't REPLs. You should've said that they aren't good or useful REPLs.

            Your argument is the equivalent of saying notepad isn't a text editor because you can't edit multiple lines at once or highlight syntax. Those are features that good text editors have, but it does not mean notepad is not a text editor.

            • alphaalpha101 8 years ago

              They aren't really REPLs.

              >Your argument is the equivalent of saying notepad isn't a text editor because you can't edit multiple lines at once or highlight syntax.

              No it's the equivalent of saying that not even programme that can possibly, technically edit a text file is a text editor.

              Python's shell thing is not a REPL.

        • lispm 8 years ago

          A 'command line interface', for example for bash, does the same. What is the difference to a REPL? Is it just another name for the same concept or are there differences? What do you think?

          • Vendan 8 years ago

            Yes, it's technically a REPL, just as bash is a scripting language.

            • junke 8 years ago

              It is a subtyping relationship. Bash's command line is a (minimalistic) REPL, but not all REPL are like Bash's command line. In most contexts, talking about REPLs is implicitly talking about REPLs that do more than just read, eval and print, however illogical that may sound.

              Note that few languages actually define READ in a user-friendly way (since python 2.6 you have the ast package, Bash's "read" returns strings).

  • jlarocco 8 years ago

    Compilation and a REPL are orthogonal concepts.

    Lots of Common Lisp systems have very good REPLs and compile to machine code. A popular example is SBCL.

  • stevedonovan 8 years ago

    Anything can have a REPL - I did one for C++ called UnderC. Unfortunately, a technological dead end and some of the worst Bison on the planet. But it was surprisingly pleasant to use C++ in a interactive way and I wrote a C++ book working from the idea that people can learn to program better in such a conversational context. Required some interesting flexibility in the usual grammar.

  • valw 8 years ago

    The compiled or interpreted nature of a language (or its runtime) is a fuzzy concept at best (Java is interpreted on the JVM in the sense that it emits bytecode that is interpreted by the JVM; the main implementation of Clojure JVM compiles Clojure to Java bytecode that eventually gets interpreted). The notion you're looking for is probably that of incremental compilation / running.

christophilus 8 years ago

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 8 years ago

    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 8 years ago

      You could just store your modified state to new variables though

      • yorwba 8 years ago

        Or 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 8 years ago

        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 8 years ago

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

          • junke 8 years ago

            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.

iLemming 8 years ago

Yes, Clojure(script)'s REPL experience is really amazing. I don't think there are any general-purpose languages (beside Common Lisp) actively being used today that even come close in comparison. What's comes with Python, Ruby, C#, JavaScript, etc. - is a feeble attempt of an interactive shell, those aren't really REPLs. Even with Haskell I was really surprised to find out that you can't "send" any selected piece of Haskell code onto GHCI.

swah 8 years ago

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

isaacremuant 8 years ago

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

justifier 8 years ago

Exportable input and output of session

djKianoosh 8 years ago

History/sessions

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

linkmotif 8 years ago

vi mode!

whipoodle 8 years ago

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 8 years ago

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

  • peatmoss 8 years ago

    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 8 years ago

      R workspaces are an idea borrowed from APL: 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.

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection