[HN Gopher] On Repl-Driven Programming
       ___________________________________________________________________
        
       On Repl-Driven Programming
        
       Author : todsacerdoti
       Score  : 313 points
       Date   : 2021-01-03 07:35 UTC (1 days ago)
        
 (HTM) web link (mikelevins.github.io)
 (TXT) w3m dump (mikelevins.github.io)
        
       | FraserGreenlee wrote:
       | I don't see how this is different from using the pdb Python
       | module? You can catch exceptions and use to write functions.
        
         | hchz wrote:
         | The biggest distinction I see is the ability to continue from
         | exceptions.
         | 
         | I find the other differences to be minor in comparison - to be
         | able to recover and continue execution following post-mortem
         | debugging is amazing.
        
       | gumby wrote:
       | Back in the early 80s a friend described this approach as
       | "programming by successive approximation." I was stung by that,
       | but I saw his point.
       | 
       | Nevertheless I felt it was wrong. The approach of designing a
       | data structure, making a few functions to manipulate it,
       | designing another, making sure they work together, etc isn't
       | really that different in lisp from C++. The loop is tighter when
       | you can interactively test.
       | 
       | Either way you have to think ahead.
        
       | avl999 wrote:
       | Is there a modern non-Smalltalk, non-Lisp (or its derivatives)
       | non functional imperative programming language that supports a
       | "real repl" that the author is talking about? I don't know if
       | that kind of environment is for me but I'd love to give it a
       | spin.
       | 
       | I have never found the REPL (the Python/Ruby REPL which is not a
       | "real repl" according to this article) to be that useful beyond
       | quickly playing with API of a library with sample data and
       | getting a feel of it and its return types. So this version of
       | repl described in the article does sound interesting.
        
         | tlarkworthy wrote:
         | https://news.ycombinator.com/item?id=25620899 (JavaScript)
        
         | dnautics wrote:
         | Bash?
        
           | eikenberry wrote:
           | Shells in general are a great example IMO. They are what I'd
           | maybe call repl-first environments, in that they are used
           | primarily as interactive repls but can be used to write
           | programs.
        
         | tieze wrote:
         | In Emacs you can set up Python in a (quite neutered) similar
         | way. Whenever I change a function, I just evaluate the whole
         | buffer to load it in the attached Python process, and will
         | switch over to the Python process to play with it if need be.
         | 
         | Also, Forth sports a repl.
        
         | sgillen wrote:
         | Julia certainly places a lot of emphasis on the repl. It
         | doesn't have break looks that I'm aware of although I really
         | wish it did!. It's a specialty language though IMO, if you're
         | not doing something scientific I'm not sure it makes much sense
         | to learn.
        
         | CraigJPerry wrote:
         | I'm thinking a SQL console mostly qualifies per the article's
         | idea of a repl. The break loop concept isn't perfect there
         | though.
         | 
         | I've always liked the python repl, breaking out a django repl
         | and being able to manipulate the db via the api always felt
         | like a super power to me when coupled with the expressiveness
         | of basic python but last year i started dabbling with Clojure.
         | My first "real" repl driven development experience.
         | 
         | 2 things stood out for me:
         | 
         | 1. You end up with some genuinely useful executable
         | documentation and it just makes sense to me to preserve some of
         | what i did in the repl while developing. I saw this first here:
         | https://github.com/stuartsierra/component/blob/master/dev/ex...
         | 
         | 2. Integration with your editor is essential - having a
         | keystroke to send a form to the repl or evaluate in-line is a
         | real productivity boost. It's like writing code while an
         | intelligent debugger is permanently live. You don't need to
         | wait for a compiler run then parse the feedback, it's just
         | instant in-line runtime feedback. "Paredit" is really sweet. I
         | used calva in vs code for this and tbh it's as attractive as
         | the language in some ways. I have no idea how to meaningfully
         | apply those concepts to something like python or javascript
         | though. They just don't lend themselves to that kind of
         | structural editing.
        
           | moocowtruck wrote:
           | have you ever tried something like https://gtoolkit.com/
        
         | cutler wrote:
         | There's Hy (hylang.org) for Python which comes with a real
         | repl.
        
       | keymone wrote:
       | Author describes a nice feature, but that's not what makes repl-
       | driven development tick for me. Repl really shines when editor
       | integrates with it - send a form to evaluate, instantly get
       | result back into the editor, eval a form and replace it with the
       | value received, define and redefine the environment on the fly,
       | get instant feedback if your code works without having to drop
       | into shell and run some test command, micro-tests and usage
       | examples that you can actually run inside editor to see how they
       | work.
       | 
       | You can get similar integration with ruby and python, but there's
       | way more friction because that kind of development is not the
       | blessed way and so nobody bothers to make it work.
        
       | qznc wrote:
       | > If it now works correctly, then congratulations; you found the
       | problem!
       | 
       | This notion if "correct" is "finished the current example as
       | intended". Running a suite of unit tests would give me more
       | confidence.
       | 
       | Even that is only for programming in the small and that is
       | comparatively easy to programming in the large. I don't see how a
       | REPL would help there.
       | 
       | That means a REPL makes easy things easier and hard things are
       | unaffected. Does not sound like a competitive advantage to me.
        
         | coldtea wrote:
         | > _Even that is only for programming in the small and that is
         | comparatively easy to programming in the large. I don't see how
         | a REPL would help there._
         | 
         | Because the right way of programming in the large is
         | programming in the small and combining things (e.g. functions,
         | components, classes, whatever, etc.), not building some huge
         | monolithic monstrocity (this is orthogonal to monoliths vs
         | microservices btw).
         | 
         | Plus, this "programming in the large", when it gets to, say,
         | 10.000.000 lines of code will still have bugs and behavior you
         | need to check/search for/and fix in individual parts, and this
         | "restarting/dynamic change" REPL will still be a great tool
         | here.
        
       | djhaskin987 wrote:
       | What a surprise, the functional folks are arguing that
       | 
       | "Because your language is different than mine, therefore it's not
       | as good as mine."
       | 
       | I've heard people say that Java or C++ isn't a language, because
       | it doesn't have features x-y-z when Racket does. Garbage.
       | Similarly, this guy is saying that because Python's shell doesn't
       | have breakloops, it isn't a _real_ REPL. Also garbage. 99.9% of
       | the time when using REPLs /shells, if something doesn't run, I
       | correct the code, copy and paste it into the shell, and run it
       | again. Breakloops don't exist in the Python shell because they
       | don't _need_ to. Python shell works about as good as Clojure REPL
       | for me.
       | 
       | I get really tired of these elitist holier-than-thou arguments.
       | Functional programming is a thing. Procedural programming is a
       | thing. REPL is a historical name... for an interactive language
       | shell. They really are quite equivalent.
        
         | mikelevins wrote:
         | You misunderstand me.
         | 
         | I am not saying that your language or your way of working is
         | not as good as mine. I'm not saying that you or anyone else
         | should discard your ways of working and adopt mine.
         | 
         | I'm not saying that Python's repl isn't actually a repl. I'm
         | not saying that everything needs to have breakloops. I'm not
         | saying that I or anything I like is any holier than anyone
         | else.
         | 
         | What I'm saying is I like a specific style of programming and
         | it makes me happy. I think more people should know about it
         | because it will make some of them happy, too. I would like
         | that.
         | 
         | Besides just liking to make people happy because that's the
         | kind of personality I have, I would also like it if demand for
         | tools that support my preferred way of working increased, so
         | that there are more of them available over time rather than
         | fewer.
         | 
         | And, finally, I like to use the phrase "repl-driven
         | programming" for the style I like because some time ago someone
         | asked about what distinguished old-fashioned Lisp and Smalltalk
         | environments from others that the questioner was more familiar
         | with, and "repl-driven programming" was a phrase that person
         | used. So I adopted the terminology. I think it's a reasonably
         | good piece of jargon for its purpose.
         | 
         | We can sensibly distinguish a repl-driven environment--that is,
         | an environment whose design is driven by the requirements of
         | interactive programming with a read-eval-print loop--from an
         | environment that merely has a repl as one of its affordances.
         | 
         | If the distinction isn't meaningful to you, then presumably
         | "repl-driven" design isn't either, and you're presumably not
         | one of the people I'm talking to.
        
       | tmsh wrote:
       | Having gone through Rustlings these past few days (via
       | https://users.rust-lang.org/t/best-way-to-learn-rust-program...),
       | I feel like the speed of the Rust compiler has gotten a lot
       | faster since I looked at it a few years ago.
       | 
       | And the compiler messages (once you get the hang of it; and the
       | investment in learning the syntax is so trivially worth it in the
       | long run) are so much richer and more helpful than other
       | languages. So it really does "feel" like a REPL or most of the
       | way there as a REPL (and I have one of the few wikis at our work
       | advocating for Common Lisp that I wrote a year ago, and still
       | actively follow Clojure groups, etc.).
       | 
       | It's just Rust starts and runs so fast (and Cargo makes it easy
       | and clear like Go); so that being able to re-run periodically
       | really fast is like a REPL. Yeah there's no retained state or
       | ability to keep a REPL running for days, etc. (though Tmux.. and
       | __nix as a repl.... more on that later), but it 's reached a
       | point where compilation is an OOM faster than before, so the loop
       | of: read, eval, print now can include the compiler.
       | 
       | And you get this amazing boost where if you're operating e.g. on
       | large data (if you're a senior engineer, like Dan Luu blogs
       | https://danluu.com/, you're often scanning codebases or log files
       | or other metadata to answer questions); so being able to tweak
       | and re-run even without caching at OOM faster speeds than other
       | languages is wonderful.
       | 
       | I can create complex refactoring scripts in Rust on GBs of data
       | and constantly re-run them until it's just right.
       | 
       | To my knowledge you can learn the ins and outs of CL or Rust;
       | both fairly specific-knowledge-heavy languages at first (Rust
       | with some syntax, CL with its non-uniformity of functions). It
       | just seems like Rust is the future. It sort of "merges" dynamic
       | languages with REPLs and *nix (with tmux) as the REPL. Pretty
       | exciting.
       | 
       | And if someone wrote a CL-like REPL with something like SLIME/SLY
       | for debugging for Rust it'd be kind of game over. And I could see
       | that coming in the next few years
       | (https://github.com/google/evcxr seems cool, but perhaps the
       | dependency on Jupyter complicates it a bit).
        
       | twobitshifter wrote:
       | For Ruby users, pry gets you most of the features discussed in
       | this post. You can get the error break loop, and drop into the
       | live environment with binding.pry. Code can be added and
       | expressions adjusted at this point. It's very powerful and the
       | only "debugger" I've ever needed for Ruby.
        
         | mikelevins wrote:
         | I've used it a good bit. When I'm using it, I miss the tools I
         | have in Smalltalk and Lisp environments.
         | 
         | When I use Smalltalk or Lisp, I don't miss pry or the other
         | tools I have when working with Ruby.
         | 
         | I do like Ruby, though. I probably like it better than any
         | other language that isn't made of s-expressions. (Well, I like
         | ML a lot, too.) But the whole time I'm working with it it just
         | makes me want to write another Ruby implementation, but this
         | time on top of a proper interactive runtime.
         | 
         | There is one, actually: MagLev, which is built on a Smalltalk
         | runtime. As far as I can tell, though, it's dormant.
        
       | civilized wrote:
       | How is this different from, say, programming in Python and being
       | forced to drop into a debugger whenever there is an error? The
       | debugger also lets you see everything in the stack, modify
       | definitions, etc.
       | 
       | MATLAB and R both have options to drop you into a debugger if
       | there is an error. Not sure if Python's debugger can do this, but
       | other than that, this style of programming is perfectly doable
       | outside of Lisp and Smalltalk.
        
         | mikelevins wrote:
         | So go ahead and set up that working situation and try this:
         | 
         | Define a function foo() in one module that calls a function
         | bar() in another one, but don't define bar().
         | 
         | Now call foo().
         | 
         | Now, in the debugger that you break into, give a definition for
         | bar(), and resume the execution of foo(). Don't quit the
         | program and restart it; that's cheating!
         | 
         | If that works fine, please tell me what tools you're using,
         | because they'll materially improve my tools for some work I
         | need to do.
         | 
         | Also, try this:
         | 
         | Define some classes and make a bunch of instances of them.
         | Write a few methods on the classes. Start your program running
         | with code that uses those classes and methods.
         | 
         | Now change one or more of the class definitions. Don't stop and
         | reload the program! Again, that's cheating.
         | 
         | When you land in the debugger (you do land in an interactive
         | debugger, right?) use it to inspect the stack and find out
         | which classes and methods are on the stack and what's
         | responsible for the breakage. Ask the Python runtime to show
         | you all the functions, types, and values that are on the stack,
         | and when you find the likely culprit, ask it to take you to the
         | source for the version that's currently on the stack.
         | 
         | Now, while you're still suspended in the call stack, redefine
         | the offending classes and methods and tell Python to use the
         | new ones. Now resume the broken computation. If you hit another
         | problem, use the same tools to find and fix that one, too.
         | 
         | After you've done all that, please let me know what your Python
         | toolchain is so that I can use it, too.
         | 
         | Also, if that's all perfectly doable in Matlab and R, that's
         | good to know. Let me know about those tools, too. I haven't had
         | much occasion to use them, but that might have to change if
         | they work that way, too.
        
           | tylermw wrote:
           | I don't know about MATLAB, but everything you describe is
           | trivial to do in R. Being dropped into the debugger is
           | standard on most R installs (it's an option you can turn off)
           | and everything you mentioned can be done in the REPL.
        
             | mikelevins wrote:
             | That's good to know; thanks!
        
       | dm319 wrote:
       | This is what makes R (and Julia I think) so special (where
       | everything evaluates to an S-expression). Having REPL for
       | exploratory data analysis is especially useful.
        
       | zmmmmm wrote:
       | I have a concern about REPL driven development, which is around
       | how maintainable the result is.
       | 
       | The reason people like REPLs is because they can experiment to
       | discover the right way to write the code in a very fast feedback
       | loop. This alleviates a huge pain when you are operating with
       | uncertainties or at the edge of your knowledge - 'does this
       | function return null if no matching entries or an empty list?' -
       | etc.
       | 
       | The problem is that the reader of your code is not going on that
       | journey with you. They are coming in naively without knowing all
       | of that experimentation you did. The writer now has the
       | opportunity to create code that "just happens" to work by a magic
       | of coincidences and conveniences based on very subtle non-obvious
       | properties of the APIs and systems they are working with. The
       | reader is severely disadvantaged and will have a much harder time
       | to reach parity with the state of knowledge that the writer had.
       | For example, some property of a function or object may not be
       | documented at all, but through the REPL the writer has
       | ascertained it is true. How can the reader know this?
       | 
       | So its a double edged sword in that the more you empower the
       | original creator of the code, the greater imbalance you create to
       | the later readers and maintainers of the code. Used well and with
       | great discipline I could see it being very powerful and positive
       | force to make better code. But in the worst case scenario, you'll
       | end up with extremely unmaintainable code.
        
         | IlliOnato wrote:
         | I think your concern is valid, but I think it's the general
         | case with any powerful tool: it can be abused, or shoot you in
         | the foot, and requires discipline to apply.
         | 
         | I don't have REPL-driven development experience, but I think we
         | are all familiar with StackOverflow. This is a great, great
         | resource, but it lead scores of programmers to the "style" of
         | development, where they mindlessly copy solutions from SO,
         | without understanding of how and why they work.
         | 
         | I guess one can extend the old maxim "there is not now, nor
         | ever will be a programming language that makes it easier to
         | write a good program than a bad one"...
         | 
         | (edited grammar)
        
         | discordance wrote:
         | Could this issue of expressing the journey be addressed by
         | leaving code/markdown breadcrumbs in Juptyer notebook like
         | cells?
        
         | reddit_clone wrote:
         | When a certain thing is done in repl, it usually converts into
         | a function or two in the source code. The ad-hoc testing code
         | is converted into unit testing.
         | 
         | That should help with maintenance no?
         | 
         | One big theoretical advantage I see in repl driven development
         | is, I can be sure that _all_ the code has been executed at one
         | time atleast. (In a normal edit-compile-link-test language
         | cycle, I can not be sure of that)
        
           | Scarbutt wrote:
           | Sure, but the point is that it requires more discipline,
           | since the REPL can encourage you to be more lazy/sloppy.
        
           | xapata wrote:
           | Yep, just copy-paste a bit of REPL history for a docstring,
           | then use `doctest` to make sure it holds. Pretty soon you'll
           | have ~100% test coverage. Minus the error paths you didn't
           | test in REPL.
        
         | tareqak wrote:
         | If there was a way to "playback" the REPL and store it for
         | later, then it would be possible for the subsequent users (or
         | even the same user) to see how the person using the REPL got to
         | that point. It would not be a replacement for documentation,
         | but it could be in addition to documentation or even a starting
         | point.
        
         | heresie-dabord wrote:
         | I agree with you that the immediate start-up and feedback is a
         | great benefit to the coder. This is why I dislike complex,
         | Rube-Goldbergian REPL systems.
         | 
         | There is a use-case for a throw-away interaction with a REPL.
         | For example, how does $builtinFuncX work, or how would $data
         | best be imported into a structure?
         | 
         | A REPL can also be a good initial approach to a more ambitious
         | problem. In this case, a REPL can be good for focus and
         | discipline.
         | 
         | If the second case is going to answer your concern and be
         | constructive, it's necessary to be able to build the code for
         | sharing and cleanly export the code for re-use.
         | 
         | I've had success tackling challenges using REPLs for Python and
         | Perl [1] in both ways. But no tooling is going to solve the
         | problem of a sloppy teammate who claims success just because
         | "it compiles" and "it works on my box". A person who knows how
         | to build good tooling goes further.
         | 
         | [1] https://github.com/viviparous/preplish
        
         | koz_ wrote:
         | The author of the code is going to have to discover how these
         | APIs work somehow, right? Why would the code end up worse if
         | the author has access to a particular tool for interacting with
         | these APIs? A shell and curl is a REPL for interacting with and
         | exploring HTTP APIs but I don't think anybody would claim that
         | the use of such tools makes code written to those APIs harder
         | to follow.
         | 
         | In fact, one of the benefits of your REPL being integrated with
         | one's editor, and being in the language of the system, is that
         | the kinds of ad-hoc scripts you write when doing exploratory /
         | debug coding which one would typically discard after use can
         | often times simply be committed, either as utility functions or
         | test cases. Far from pulling up the ladder on devs who come
         | later, having a REPL makes it more likely for them to have
         | access to the same tools used to build and understand the
         | system in the first place.
        
         | marcosdumay wrote:
         | More than Lisp, SQL is a REPL-only language. The complexity you
         | can achieve there without some interaction is very small.
         | 
         | After some on the field experience, my conclusion is that this
         | is not a large problem. Yes, you were not there on the initial
         | design and does not have all of that acquired knowledge, but
         | you can always run your own tests and formulate and verify your
         | hypothesis. The REPL is there for debugging too, not only for
         | the original design.
        
           | tester756 wrote:
           | >SQL is a REPL-only language. The complexity you can achieve
           | there without some interaction is very small.
           | 
           | ha ha.
           | 
           | unless it's giant sql with 5 sub queries and poorly formated
           | for bonus points
           | 
           | good luck
        
             | marcosdumay wrote:
             | Hum... 5 sub queries? Nope, I'm talking about bigger stuff.
        
         | juststeve wrote:
         | 100%, and that also applies to the original author.
         | 
         | Building a script from the REPL might mean running different
         | parts of the code out of order (such as reloading a function
         | definition). As you mentioned, it's easy to lose track of
         | state, writing 'clever'/unmaintainable code, and forgetting the
         | order and purpose of the code.
        
         | Buttons840 wrote:
         | Code developed in a REPL would ideally be "pure", meaning it
         | takes a value and returns a value without side-effects. Then
         | others can experiment with it to their hearts content. Maybe
         | it's ok to _read_ from a database, but don 't write. If you
         | write to the database nobody else will dare touch your code.
        
           | mikelevins wrote:
           | That rule absolutely does not work for me.
           | 
           | For me, the whole point of the repl is that I want to build
           | something incrementally, interactively, building it a little
           | at a time by making small changes to it. I want it in memory,
           | running and responding to my changes as I make them. Put this
           | here; put that there. Change that around. Let me look at it;
           | nope. Put that back where it was. Now add this.
           | 
           | If the repl is stateless then the whole purpose is defeated.
        
             | Buttons840 wrote:
             | I don't mean the REPL is stateless, but the code developed
             | using the REPL would ideally be stateless. Stateless code
             | would be easier for others to experiment with in their own
             | REPL, without "oh no, you broke production with your REPL!"
        
               | mikelevins wrote:
               | You can use a stateful repl to develop stateless code,
               | but in order to provide the features of an old-fashioned
               | Lisp or Smalltalk system, the kind of system I prefer to
               | work with, you can't make any stateful changes off limits
               | because there's no way to know in advance what changes
               | you're going to need to make.
               | 
               | As you're rummaging around through the dynamic state of
               | the running system, you'll discover changes that need to
               | be made, and you might discover them absolutely anywhere.
               | Sure, you could always kill the running system, make the
               | change to the sources, and rebuild the system, but that's
               | exactly what we're trying to avoid.
               | 
               | Consequently, old Lisp and Smalltalk systems are allergic
               | to restrictions on runtime changes. Loosely speaking, if
               | I find something I can't change while my program is
               | running, that restriction is a bug in my development
               | environment.
               | 
               | Old systems like this will _discourage_ certain kinds of
               | changes because they 're usually ill-advised, but will
               | not _forbid_ them, because forbidding them is anathema.
               | 
               | As an example, several Common Lisps implement __package
               | locks __on certain system packages. A __package lock
               | __prevents you from changing the definitions of system-
               | defined constructs.
               | 
               | But it's Lisp, so it doesn't _really_ prevent the change.
               | It just makes it more inconvenient. You have to say
               | "Mother, may I?" first, which gives you the opportunity
               | to soberly consider whether making that specific change
               | is really really what you want to do.
        
               | Jtsummers wrote:
               | You don't connect (necessarily) the REPL to production.
               | The code can be stateful code if you're not careless.
               | Just like you oughtn't log into the production database
               | server and write/update/delete data entries directly.
               | 
               | This is about the development phase more than the
               | production phase. Do something like this (directly in the
               | REPL or in a buffer as mikelevin has described doing
               | elsewhere):                 (defparamater *db* (connect-
               | to-test-db))       (query-db *db* (make-query some-query-
               | description))       ;; see that it pulls out the desired
               | record       (let ((record (query-db *db* (make-query
               | sqd)))         (update-record record)         (write-to-
               | db *db* record)) ;; update the entry       ;; rerun the
               | query above to see that things changed as expected
               | ;; rewrite that let as a defun:       (defun make-update-
               | to-some-record (sqd db)         (let ((record ...))
               | ...))        ;; test that it works        (make-update-
               | to-some-record sqd *db*)        ;; see that it does in
               | fact do the same as the let above.        ;; move that to
               | a permanent source file, and build it        ;; into the
               | system.        ;; move tests into test file.        ;;
               | repeat with next feature/task
               | 
               | The functions run in the REPL are stateful, but we aren't
               | careless about what they're touching.
               | 
               | NB: You _can_ connect to production environments. You can
               | even connect to live, production lisp images. This could
               | be useful for: profiling real-world activity, debugging
               | real-world problems, applying hot-fixes. But especially
               | that last one should be done carefully, and ought to have
               | been validated using a test environment first.
        
               | reddit_clone wrote:
               | Right. I usually start with creating global objects in
               | the repl. Write little snippets in the repl to operate on
               | these. The snippets end up with all these objects
               | parameterized as functions. Which I can test again from
               | the repl itself.
               | 
               | Turn the testing code into unit test as I go along.
               | 
               | Rinse. Repeat.
        
         | dragonwriter wrote:
         | > For example, some property of a function or object may not be
         | documented at all, but through the REPL the writer has
         | ascertained it is true. How can the reader know this?
         | 
         | Well, the writer could document their code.
         | 
         | Or, the reader could discover behavior of undocumented code the
         | same way the writer did; presumably the "reader" will also have
         | a REPL available.
        
         | thewebcount wrote:
         | > But in the worst case scenario, you'll end up with extremely
         | unmaintainable code.
         | 
         | Actually, isn't it far worse than that? Say you're stopped at a
         | function call, you call it, and it does the wrong thing. You
         | edit a variable that's passed as an argument to the function
         | and call it again. Now it does the right thing. Great! You
         | fixed the bug, right? Except that you don't actually know that
         | your program can actually ever end up in that state naturally.
         | 
         | Where did that variable originally come from? Was it entered by
         | the user? Was it read from a file? Was it from a table? If so,
         | does the table contain the modified value? This seems like a
         | recipe for problems to me. I'm sure it's fine in simple cases
         | where you're just changing the value of a constant or
         | something, but it seems really likely to lead to incorrect
         | reasoning about the code while you're working with it in any
         | but the simplest of cases. Am I blowing this out of proportion?
        
           | mikelevins wrote:
           | Well, no, you didn't fix the bug. You collected a datum about
           | the bug: it caused a variable to have a value that it should
           | not have had. Now you know something about it that you maybe
           | didn't know before.
           | 
           | When you say "Great! You fixed the bug, right?" it sounds
           | like a paraphrase of a passage in my essay that I had some
           | misgivings about when I wrote it. But I thought, surely
           | nobody's going to think I really mean the problem is
           | definitely solved? Surely, people will realize its a bit of
           | hyperbole.
           | 
           | Maybe not. Maybe I should have instead written it more
           | soberly. Maybe I should have just said "Now you've collected
           | a bit of data about the nature of the bug."
        
         | discardable_dan wrote:
         | I have worked on massive LISP projects that were developed
         | using REPL-driven development, and your fears are critically
         | valid. Even worse, this basically hamstrings it as a standalone
         | application. The workflow to _use_ the application becomes
         | "start the REPL under emacs and launch it", which is, in my
         | personal opinion, a completely unacceptable distribution model.
         | And if you don't use emacs, good luck to you! Once set up as
         | REPL-based development, Smalltalk and LISP both have to
         | essentially ship their "whole environment" to deploy the
         | application. This only works out well for small projects that
         | do not need to be distributed, or whose distribution you, the
         | developer, have a lot of control over.
         | 
         | (There's an aside to be said about building binaries from a
         | LISP compiler, but most binaries I have seen produced by such
         | compilers still need access to the required libraries, which
         | sort of hoses it as a distribution solution. This could be a
         | result of the build system used on the projects I work on.)
         | 
         | And your documentation concern is valid, but I think even worse
         | is the design concern: when designing an API, you have to stop
         | and design it and consider the various concerns. If you develop
         | it as you need it / as you go, it is possible to arrive at sub-
         | optimal designs. Hacking the "next piece" out at each step is
         | likely to lead to slanted designs with poor APIs, requiring
         | future refactoring. You see this with programmers at every
         | level: if you ask them to write a program to do a single thing,
         | then add more (related) capabilities, and continue ad nauseum,
         | at some point the program will need to be refactored. This
         | suggests that, without prior planning, REPL-based design is set
         | up to incur technical debt. Unfortunately, my real-life
         | experience confirms this. Moreover, refactoring something in a
         | REPL is... a chore, to say the least.
        
           | mikelevins wrote:
           | Your observations are valid, and need to be kept in mind when
           | developing in an exploratory, interactive way.
           | 
           | Your alarm seems a little overblown. Maybe it reflects a
           | particular bad experience? I've delivered a bunch of products
           | built in the way I prefer to work, and it's been a long time
           | since I've had the problems you're describing. Maybe that's
           | because I ran into such such problems early in my career and
           | learned ways of handling them.
           | 
           | And yeah, if you just iterate and explore, you can wind up in
           | lacunae. You do need to develop some actual goal and a vision
           | of how to get there. Interactive development can be really
           | helpful for the exploring part, but it can't pick your goals
           | for you.
           | 
           | In short, interactive development is a way of working that's
           | good for some cases and some people (me, for instance). It's
           | not a cure-all or an objectively superior methodology for all
           | people or all cases. I don't want it to take over the world.
           | I just want it to continue to be an option because it's the
           | way I prefer to work.
        
             | Folcon wrote:
             | Echoing this, I'd really like it to be an option in other
             | languages, because it's just a pleasant way to work and
             | it's nice to have the option.
        
             | O_H_E wrote:
             | Oh man, I appreciate your pragmatism and open-mindness. It
             | is refreshing to see someone advocate for something without
             | trying to "take over the world" and proving others wrong.
             | 
             | Thanks for sharing your experience amd positivity with the
             | world.
        
         | TheAnswerMan wrote:
         | So REPL may make people less likely to document the code?
         | 
         | I don't see the correlation.
        
         | avmich wrote:
         | > the more you empower the original creator of the code, the
         | greater imbalance you create to the later readers
         | 
         | REPL gives you more tools to shoot yourself in the foot, but
         | you're not required to use those. Most programmers can write
         | spaghetti code in many Turing-complete languages - not that
         | they do that all the time.
         | 
         | In other words, REPL gives you powerful tools - which, yes, can
         | be misused. They are still powerful, and can bring success when
         | used well.
        
         | wtetzner wrote:
         | I really don't follow. If you're programming in a language
         | without a REPL, don't you have the exact same problem? If the
         | author doesn't document their knowledge, then it's lost for
         | readers.
         | 
         | I don't really see how a REPL makes any difference there.
        
       | alknemeyer wrote:
       | For what it's worth, IPython's "autoreload" magic and Julia's
       | "Revise" package get you _closer_ to what the author describes.
       | In both cases, one can define functions/classes/structs in a
       | file/module, load it in a repl, create objects etc, modify the
       | file and have the changes propagate through to live objects
        
         | joshlk wrote:
         | You can also add breakpoints which automatically occurs when
         | exceptions are raised. Which is similar to the breakloop
         | functionality mentioned in the article.
        
           | anentropic wrote:
           | I find ipython + ipdb super useful in this regard
           | 
           | But AFAIK neither provide the feature described in the
           | article where you can continue running after an unhandled
           | exception.
        
             | jmuhlich wrote:
             | True, but running the %debug magic command in IPython
             | immediately after an unhandled exception drops you into the
             | debugger at the point of the raised exception, complete
             | with the full interpreter state including the stack. It
             | doesn't permit edit-and-continue but you can still evaluate
             | expressions (including calling any function) to explore
             | what went wrong without having to recreate the situation
             | inside an explicit pdb session.
        
           | djm_ wrote:
           | Yes, running pytest with the --pdb flag will drop you into
           | the debugger on an unhandled exception which gets a
           | comparable workflow but it's not quite the same as, a)
           | writing tests to file first is not repl-driven development,
           | and b) you generally have to think about doing it first.
           | 
           | In an ideal repl-driven world you could write the test in the
           | repl entirely and commit it to disk once you're ready.
        
         | globular-toast wrote:
         | Closer but still so far. As soon as you have more than one
         | module in Python it completely falls apart, despite any
         | autoreload magic. It just isn't the same at all as REPL based
         | programming. In Lisps you can write your entire program with an
         | instance and a REPL running the entire time. No restarting the
         | instance required. It works because it's just how Lisp works. A
         | REPL is a trivial thing you can write yourself that runs in the
         | same instance.
         | 
         | Whenever I think about it I can't quite put my finger on
         | exactly what Python would need to make it the same. It's
         | something to do with the way imports and namespaces work,
         | though.
        
           | sedachv wrote:
           | > Whenever I think about it I can't quite put my finger on
           | exactly what Python would need to make it the same. It's
           | something to do with the way imports and namespaces work,
           | though.
           | 
           | Python has a fundamentally broken module system, and Python 3
           | did nothing to fix it:
           | 
           | https://nedbatchelder.com/blog/201908/why_your_mock_doesnt_w.
           | ..
           | 
           | https://docs.python.org/dev/library/importlib.html#importlib.
           | ..
        
             | globular-toast wrote:
             | Right. That's it. Thanks for the links!
        
       | miccah wrote:
       | I admit I have not experienced true REPL driven development as
       | the author defines it, but I have created an easier way for
       | "development with a REPL" in vim. I love it because it's like a
       | looser, faster Jupyter notebook, and it works with any language
       | that has a REPL.
       | 
       | It works by sending highlighted text to vim's terminal buffer. I
       | wrote a short blog post with a demo and the vimscript code here:
       | 
       | https://mcastorina.github.io/posts/vim-repl-driven-developme...
        
         | miccah wrote:
         | I should have read more comments before posting, but it seems I
         | reinvented an inferior wheel:
         | 
         | https://github.com/jpalardy/vim-slime
        
       | magv wrote:
       | For those of us that don't use Lisp (or Emacs+SLIME), and are
       | stuck with Python/Julia/Lua/etc and Vim may I give a practical
       | recommendation? The Vim-Slime plugin [1] is a half-decent way of
       | making interactive development work. With a bit of tuning you can
       | make it start a terminal emulator window inside Vim, and have it
       | send the current paragraph of your source code into it with a
       | press of a button.
       | 
       | While not as great as working with a proper Repl-oriented
       | language (as the article explains), this is still so much more
       | pleasant than having to re-run the whole program every time you
       | change a function.
       | 
       | [1] https://github.com/jpalardy/vim-slime
        
         | alpaca128 wrote:
         | > With a bit of tuning you can make it start a terminal
         | emulator window inside Vim, and have it send the current
         | paragraph of your source code into it with a press of a button.
         | 
         | Defining keybindings for splitting the window, opening a
         | terminal buffer and copying a paragraph does not really require
         | a plugin. That's one of the features I like most in Vim, its
         | ability to just map any input to another sequence of keypresses
         | can replace whole scripts and plugins provided you're fine with
         | readability comparable to regular expressions.
        
         | dm319 wrote:
         | And nvim-R for vim/nvim and R.
         | 
         | [1] https://github.com/jalvesaq/Nvim-R
        
         | pjmlp wrote:
         | Without caring about VIM, this would be my options,
         | 
         | Python IDE tooling on Microsoft stack.
         | 
         | https://docs.microsoft.com/en-us/visualstudio/python/python-...
         | 
         | Juno IDE for Julia
         | 
         | https://junolab.org/
        
         | nikhizzle wrote:
         | I also recommend jupyterlab for a similar experience. I was a
         | hardcore vim terminal guy for 17 years, but now I won't be
         | going back.
         | 
         | The ability to interactively develop functions in almost any
         | language inside the same environment had been revolutionary for
         | my productivity given my short attention span.
        
           | alpaca128 wrote:
           | If you're using Jupyter within the browser you can have both
           | via the FireNVim plugin in Firefox. It connects to NeoVim in
           | the background and lets you edit any multi-line text field
           | with 100% of your personal Vim configuration, the only
           | limitation is that the color scheme cannot be changed.
        
           | Jugurtha wrote:
           | Would you mind sharing what you're doing with JLab? Do you
           | use it in machine learning or are you doing something
           | completely different?
        
       | GianFabien wrote:
       | AFAIK both Lisp and Smalltalk environments are image based. That
       | is, all source code and all objects are stored in memory. You
       | need to save that image in order to continue from where you left
       | off. Snapshots are also used to allow rollback to previous "good"
       | states.
        
         | pjmlp wrote:
         | Dart, Java and .NET have a kind of edit-and-continue without
         | images.
         | 
         | It is all a matter of tooling.
        
           | andybak wrote:
           | > It is all a matter of tooling.
           | 
           | I think the point of the article is that tooling can only get
           | you so far. The abstraction they attempt to provide is leaky
           | if the support for this style of development isn't firmly
           | designed into the entire system.
        
             | pjmlp wrote:
             | Yeah, but in that regard developer culture is what matters.
             | 
             | I have seen very few people actually using these kind of
             | workflows back when Smalltalk and Lisp were more relevant
             | (I used Smalltalk/V back then).
             | 
             | It is like using gdb, many don't go beyond step, next,
             | print, run, breakpoint and discover how powerful it
             | actually is (same applies to other debuggers).
        
               | mikelevins wrote:
               | I am indeed claiming that tooling only gets you so far,
               | if you don't have good support designed into the runtime.
               | 
               | I take you at your word that you didn't use the sorts of
               | workflows I've described, but I did, and I still do, to
               | the extent that I can, and it was common place among, for
               | example, the programmers working on the bauhaus OS or the
               | ones working on the SK8 development environment.
               | 
               | We kept images around as a matter of course for various
               | purposes, for example.
        
         | phoe-krk wrote:
         | _> That is, all source code and all objects are stored in
         | memory._
         | 
         | Common Lisp usually stores code in form of normal source files
         | that are then possible to load into a clean-slate Lisp image.
         | It's possible to dump images and restore them, but it's not the
         | norm of working with CL.
         | 
         | Unlike in Smalltalk, I currently know of no tools that allow
         | one to easily edit source code of functions/methods that have
         | already been compiled into the system, and therefore the
         | dependency on the filesystem source files is heavy and
         | immediate.
         | 
         | AFAIK dumping images in CL is mostly used for shortening load
         | times by preloading code and data, and for application
         | delivery, but not for live editing support.
        
           | brainbag wrote:
           | It's not compiled, but Ruby's Pry REPL allows you to edit and
           | update code files during live execution. I use it all the
           | time for adding breakpoints and live fixing issues. It's very
           | productive for problem solving.
        
           | lispm wrote:
           | > Unlike in Smalltalk, I currently know of no tools that
           | allow one to easily edit source code of functions/methods
           | that have already been compiled into the system
           | 
           | Lots of Common Lisp systems support that in some way. Those
           | record the location of the source code together with the
           | machine code. These locations are stored in the image.
           | 
           | For example the standard CL function ED takes a function name
           | and in many Lisp systems this will edit the function in some
           | implementation specific way. In Emacs one uses M-. to get the
           | definition of a function. In those implementation it will ask
           | the Lisp for the location of that function source code.
           | 
           | The one system that worked similar to Smalltalk is Interlisp.
           | See https://interlisp.org . It recently has been open sourced
           | and is in the process of making it more accessible. It's a
           | glimpse into an alternative world of computing from the past.
        
             | phoe-krk wrote:
             | I know that many implementations record source location,
             | but the source itself is still on the filesystem, not in
             | memory, therefore making source locations useless if we
             | move the filesystem out of the equation.
             | 
             | I also know that many implementations store the forms in
             | FUNCTION-LAMBDA-EXPRESSION, but as I said, I know of no
             | _easy_ way to edit these in-memory forms either. Interlisp
             | has had a structure editor to work with those, but that
             | utility was not ported back into the CL workflow. I hope it
             | eventually will be, with Interlisp being open sourced now.
        
               | lispm wrote:
               | > therefore making source locations useless
               | 
               | Just make source available on the file system. Mount it.
               | Copy it.
               | 
               | That's why Common Lisp has logical pathnames. Typically
               | one uses logical pathnames in source locations. When I
               | set up my application on a machine, I define a
               | translation table to point to the source code.
               | 
               | One can also just use standard pathnames and mount the
               | source code to a standard path. That's how one also has
               | set up Lisp systems in clusters. Every machine mounts the
               | source in a standard path (or a logical path) and a
               | client system will then have access to that source code.
               | The image still has the recorded locations.
               | 
               | Btw., even Smalltalk stores its source code not in the
               | image. The Smalltalk source code is stored OUTSIDE of the
               | image in the file system and it has to have the files
               | available and needs to know the location of these source
               | files.
               | 
               | See: https://squeak.org/downloads/                   The
               | Squeak/Smalltalk programming system consists of three
               | parts:              * a virtual machine for your
               | platform,         * both image and changes files of a
               | particular version, and         * a sources file for the
               | particular image file.
               | 
               | The source and changes files contain the source code of
               | the running Smalltalk. An edit of source code will then
               | lead to an entry in the changes file.
               | 
               | Smalltalk usually can decompile byte code, so one can
               | edit code which has no corresponding source code, with
               | some loss of original source information.
        
       | dragonwriter wrote:
       | > The catch is that the designers of your language system had to
       | think that facility through in the planning stages. You don't get
       | a decent implementation of it by bolting it on after the fact.
       | Breakloops need full access to the entire development system,
       | interactively, with a computation and its call stack suspended in
       | the breakloop's environment.
       | 
       | Interestingly, while Ruby's default REPL (irb) doesn't have this,
       | and Ruby's (and irb's) designers obviously _didn't_ think this
       | facility through in the planning stages, Ruby _does_ have an
       | alternate REPL (pry) which has a lot of the advanced REPL
       | functionality irb is missing but _also_ doesn't have this
       | function built-in, but which does have a separate add-on (pry-
       | rescue) which provides it.
        
       | milansuk wrote:
       | REPL is very useful, but few months ago I realize[0] that setting
       | a break-point is not easy as people think, because the program
       | can visit the line through multiple paths(function can be a call
       | from different places), so you can spend a lot of time
       | debugging/finding the right path to the line, which means that
       | you probably have to set more break-points to get the program to
       | the right place faster.
       | 
       | [0] https://skyalt.com/blog/repl.html
        
         | aidenn0 wrote:
         | In lisp I do something like:                 (when (in-state-i-
         | care-about)         (break))
         | 
         | Many non-lisp debuggers have conditional breakpoints, though
         | they often have a larger performance overhead than the above.
        
       | khalilravanna wrote:
       | What are people's thoughts on how using vs not using a REPL help
       | or hinder one's _thinking_ as a programmer. Specifically I mean
       | if you have no REPL and maybe a long compile time you're forced
       | to put a little more thinking and planning in up front if you
       | don't want to waste your time. You might be more meticulous in
       | catching bugs. Whereas with a REPL you can throw stuff at the
       | wall. If something breaks you can just tweak variables and
       | structure in real time till it starts working.
       | 
       | That's just one characterization I came up with off-hand and it
       | may be inaccurate. I'm mostly trying to paint a picture where
       | tools affect the thinking of the programmer.
       | 
       | What are people's thoughts on this? Does using a REPL or not
       | using a REPL change your thinking and how so?
        
         | mikelevins wrote:
         | I think it's a valid observation. Circumstances that make you
         | think things through ahead of time compel you to learn things
         | and gain skills that you would not otherwise learn and gain.
         | 
         | The other way around is true, too, of course.
         | 
         | But the intensely-interactive style of programming-by-teaching
         | is the less common approach. In pretty much every thread I've
         | been part of about the topic there have been commentators
         | who've said they just weren't even aware of it as an option,
         | who had no idea that full-featured repl-driven environments had
         | ever existed or were a possibility.
         | 
         | What are the odds that such an obscure approach to programming
         | has already attracted all the programmers who would benefit
         | from it? Long odds, I'm guessing.
         | 
         | So my guess is that it's worthwhile to point out the option for
         | the sake of people who would benefit from it but don't know
         | they have the option.
         | 
         | For my benefit too, of course. I want more people to know about
         | it, because that increases the chances that demand will rise.
         | Rising demand increases the chances of greater investment in
         | those kinds of tools, and reduces the chances of their
         | extinction.
        
         | Jtsummers wrote:
         | > Specifically I mean if you have no REPL and maybe a long
         | compile time you're forced to put a little more thinking and
         | planning in up front if you don't want to waste your time
         | 
         | Amusingly, to me, this is something people describe as the
         | difference between having to submit punch cards to (or schedule
         | a job on) a mainframe versus having a compiler on your own
         | machine. That having such quick access to the compiler would
         | lead to the "throw stuff at the wall" approach.
         | 
         | IME, faster feedback loops _do_ increase the  "throw stuff at
         | the wall" approach, but for those of us who still (mostly) sit
         | back and think, it's an enabler and not (just) a crutch. I can
         | get in the flow much better in a language like Lisp and stay
         | there. If, for instance, there's some confusion in my mind
         | about how a function or data structure works, in C++ it takes
         | me longer (more of a constant factor longer rather than orders
         | of magnitude longer) to write something and test it out
         | (assuming documentation doesn't clear it up for me). But while
         | the time to explore it isn't _huge_ , I've been pulled out of
         | my focus for longer. With Lisp, I can test it in seconds and
         | get right back to whatever I was doing.
         | 
         | But also, when I don't have a REPL, I break my programs into
         | smaller programs (libraries/modules) which can be composed into
         | the larger program I want. This lets me, partially, recreate
         | the REPL experience. Using a test runner (a proper one or an ad
         | hoc one) and a set of small CLI apps that let me use the
         | smaller modules directly I get something approaching the
         | feedback speed of the REPL. With fewer dependencies for any
         | part under test (or as a CLI app), I get much faster
         | compilation speeds vs needing to recompile a much larger
         | program.
        
       | blunte wrote:
       | This article helps us understand what real repls enable that
       | interactive language shells do not.
       | 
       | What would be very helpful next would be a video comparison of
       | writing a program that is just complex enough to not be trivial
       | or too artificial, first using the common approach, and next
       | using repl-driven approach.
        
         | globular-toast wrote:
         | Why not just learn a Lisp yourself? You'll get far more out of
         | it than watching a video. The old adage is still true: learning
         | a Lisp makes you a better programmer.
         | 
         | Your choices are:
         | 
         | * Common Lisp: install emacs, SBCL and set up SLIME,
         | 
         | * Clojure: install emacs, Clojure and set up CIDER,
         | 
         | * Scheme: install emacs, racket (or guile) and set up Geiser,
         | 
         | * Emacs Lisp: install emacs.
         | 
         | You'll notice that they all involve emacs. There are probably
         | other ways but you want something that is quite tightly
         | coupled, which all of those emacs packages provide. REPL driven
         | programming is better served by an editor like emacs rather
         | than vim. You want something where you can quickly write/edit
         | code in one buffer and send it to the REPL with a couple of
         | keystrokes. The lower the "cost" of this operation the better;
         | it should be as easy as typing (as it becomes that frequent of
         | an operation).
         | 
         | To help you choose: Common Lisp and Clojure are the most
         | practical. They are both general-purpose languages with a
         | wealth of libraries available. Common Lisp, as the older
         | language, has far more literature available including some of
         | the best programming books ever written. Scheme is the most
         | beautiful language and has one of the best textbooks ever made:
         | _Structure and Interpretation of Computer Programs_ (SICP).
         | Emacs Lisp is the most fun, practical but also the most quirky.
         | Luckily, learning any Lisp will put you in a better position to
         | learn any other Lisp.
         | 
         | Emacs Lisp is fun because it presents the most exciting part of
         | REPL based programming: hacking a live, running system. Most of
         | the time you run a lisp instance just for the purpose of
         | development, but if your program is working and doing
         | something, then why not hack on it while it's running? There's
         | no difference between running a REPL in a "development"
         | instance and a live, production instance. When you hack on
         | emacs you are doing just that: hacking a live, running
         | instance. It's not often you get to use a tool to hack on the
         | tool itself while it's running.
         | 
         | Whatever you choose, good luck on your journey. I truly envy
         | those who have yet to experience the beauty of Lisp
         | programming. It changes you forever.
        
           | blunte wrote:
           | Nice reply. In my case, I'm already an effective amateur at
           | Clojure, but I only use the repl for testing ideas and
           | debugging.
           | 
           | In the same way there are guides that teach TDD, I would
           | enjoy seeing a RDD (repl-driven ...) tutorial.
        
             | simongray wrote:
             | In Clojure the "Rich Comment Blocks" are a very common way
             | to use the REPL: https://betweentwoparens.com/rich-comment-
             | blocks
        
             | tincholio wrote:
             | You could have a look at this video by Sean Corfield:
             | https://www.youtube.com/watch?v=UFY2rd05W2g It gives you a
             | good idea of what the REPL workflow is like. Sean will give
             | a more in-depth talk about this next week, and it will be
             | available in the London Clojurians channel afterwards, if
             | you're interested.
        
           | smashedtoatoms wrote:
           | You can use Common Lisp with VSCode pretty easily now with
           | the Alive plugin. It uses SWANK under the hood, but you don't
           | NEED emacs anymore for Common Lisp. You can also use Calva
           | with Clojure and vscode. The emacs requirement is slowly
           | going away. You get all the perks of hacking live running
           | systems, no emacs required.
        
           | coldtea wrote:
           | > _Why not just learn a Lisp yourself? You 'll get far more
           | out of it than watching a video._
           | 
           | Because one is a 30 minute endeavor and the other is a
           | weeks/years endeavor.
        
       | ashtonkem wrote:
       | Having used Clojure and Common Lisp professionally, I think
       | advocates for REPL driven programming universally overstate the
       | utility of a first rate REPL.
       | 
       | Yes, it's nicer than Python's. Yes, it's convenient. No, I never
       | ended up doing my development in the REPL _first_. Why? Because
       | editing mistakes in a REPL usually sucks, because a REPL is not a
       | text editor.
       | 
       | What I ended up using heavily was REPL to file integration, which
       | gave me the ability to write a function normally, evaluate it in
       | the attached REPL session, and then play around with it in the
       | REPL. This is far short of the "REPL driven development" that's
       | commonly discussed, and frankly something that's probably
       | possible with the Python REPL if they wanted to.
       | 
       | Editing data and functions in the REPL is a neat trick, but it's
       | a double edged sword, because a REPL can crash, and it provides
       | incredibly rudimentary support for diffing current state and
       | migrating changes back to permanent files. I would never start
       | with anything more than a trivial "how do I manipulate this list"
       | in the REPL for that reason. Oh, and we had a lot of issues with
       | REPLs getting into a bad state with Clojure due to multi methods
       | and protocols; if you're doing your primary work in the REPL,
       | then having to restart it due to it becoming unstable really
       | sucks.
        
         | xkeysc0re wrote:
         | >What I ended up using heavily was REPL to file integration,
         | which gave me the ability to write a function normally,
         | evaluate it in the attached REPL session, and then play around
         | with it in the REPL. This is far short of the "REPL driven
         | development" that's commonly discussed, and frankly something
         | that's probably possible with the Python REPL if they wanted
         | to.
         | 
         | I mean, yeah. You just import the Python module you're writing
         | and you can call any function or inspect any object you want.
         | iPython even has hot reload
        
           | Folcon wrote:
           | I'm personally of the opinion that this isn't the same thing
           | and I'm saying this as someone who wants to do this in
           | python.
           | 
           | Unless you've figured it out? In which case please say,
           | because I want to code python like this.
           | 
           | Specifically I write code in my editor, hit a keystroke and
           | all the code at the cursor gets sent to my running python and
           | evaluated. The editor needs to be aware of python's indenting
           | rules so it can do this, but once again if you've worked out
           | a nice way to do this please say. Minimal keystrokes required
           | would be great =)...
           | 
           | I'm not really requiring that the evaluated output gets sent
           | to the editor, though of course that would be ideal, because
           | in the worst case I can side-by-side the editor and the
           | terminal.
           | 
           | From my perspective jupyter notebooks get kind of close, but
           | I'm not coding in a file, so it's not quite ideal.
        
             | hirple wrote:
             | org-babel might work here. There are also repl keybindings
             | in python mode in emacs.
        
             | Izkata wrote:
             | > Specifically I write code in my editor, hit a keystroke
             | and all the code at the cursor gets sent to my running
             | python and evaluated. The editor needs to be aware of
             | python's indenting rules so it can do this, but once again
             | if you've worked out a nice way to do this please say.
             | Minimal keystrokes required would be great =)...
             | 
             | ...I call this "copy/paste". And since I use X clipboard
             | and a tiled window manager, it's only a couple keystrokes
             | and no mouse. Highlight the code in the editor,
             | "super+<direction>" to switch windows, "shift+insert" to
             | paste.
        
               | Folcon wrote:
               | I don't quite agree, that might work for your specific
               | workflow, but you can flub that sort of thing.
               | 
               | There's a reason I'm specifying specific actions, I can
               | easily see increasing the cognitive burden breaking the
               | ease of that workflow. You can't tell me that putting
               | your cursor to a code block and hitting a key is the same
               | as selection, copy, swap to console, paste...
               | 
               | I've certainly had situations where I've done what you're
               | describing and then still taken the extra time to setup
               | the workflow I've described above because it's worthwhile
               | when you're trying to debug a knotty problem.
        
             | petemc_ wrote:
             | Not a keystroke and I'm probably being captain obvious, but
             | I use pythons execfile for this.
        
         | reddit_clone wrote:
         | You don't have to type up stuff in repl literally. You can
         | write regular functions in emacs and send pieces of code to the
         | running repl.
         | 
         | For adhoc testing code you can type on the repl buffer.
        
         | blandflakes wrote:
         | I 100% agree with you and another comment:
         | https://news.ycombinator.com/item?id=25622990
         | 
         | I spent years in Clojure and found that writing code in the
         | REPL just... isn't fun. This has been true for every
         | interactive system I've ever used (including... the shell).
         | 
         | The shell as a REPL for a succinct language is nice for
         | interactive workflows, but for trying to build less ephemeral
         | pieces of code, the editor is the first class citizen I care
         | about. Sending code to a REPL to be interacted with? Cool, and
         | I _think_ this is what a lot of people mean when they say REPL-
         | driven.
         | 
         | I'd imagine convenience around that in a language or its
         | tooling would be what convinces me to do more REPL-style
         | things.
        
           | koz_ wrote:
           | I think what people mean when they say REPL-driven is to have
           | a headless REPL that one never actually interacts with
           | directly, but rather sends it snippets of code for evaluation
           | directly from one's editor. Typing code into a shell prompt
           | is excruciating by comparison.
           | 
           | I think this is a huge barrier of entry to languages like
           | Clojure. The syntax and dynamic nature of the language really
           | lend themselves to an extremely interactive and productive
           | workflow, but that is a hard thing to communicate. It seems
           | most people think that when Clojure programmers refer to the
           | joy of the REPL they imagine writing code into Python or
           | Ruby's REPLs (e.g. https://repl.it/languages/python3), but
           | that is about as far from what is meant as is possible. I
           | mean, who would enjoy programming in an environment that
           | deleted your work every time you finished a line of code?
        
         | mikelevins wrote:
         | As I said elsewhere, when I say "repl-driven programming", I do
         | not mean "programming in a repl window", or "programming at a
         | command shell". I'm talking about the runtime's read-eval-print
         | loop, not the UI's repl window.
         | 
         | When I'm working, there is little or no migrating of code from
         | the repl buffer to a file because it's already all in a file. I
         | almost always work in a file. I write snippets in a file, send
         | them to the repl with a keystroke, and build up the world
         | incrementally as I discover what it needs to be. As the
         | contents of the file get larger and more complicated, I move
         | things around and organize them. It's a conversation _with_ the
         | repl, not an editing session _in_ the repl _window_.
         | 
         | I don't consider any Clojure tools I know of to constitute a
         | proper repl-driven environment, precisely because the language
         | and runtime lack support for the kinds of programming and
         | debugging that I've taken for granted for decades. If I can't
         | inspect and edit and redefine _everything_ in the runtime, it
         | 's not the full, proper set of tools.
         | 
         | I've written a good bit of Clojure code, and I'll happily do it
         | again if I need to do something that isn't convenient in, say,
         | Common Lisp, but I consider it an acceptable alternative to
         | things like Haskell and Scala and F# and Swift, not an
         | attractive alternative to Lisp and Smalltalk.
         | 
         | I do occasionally need to restart a Lisp environment because of
         | some gnarly breakage I've committed, but it's pretty rare.
         | Moreover, killing and restarting my favorite lisps takes about
         | --wait, let me check--okay, a second and a half to kill a live
         | app in staging and have it back up, fully-functioning.
        
           | svnpenn wrote:
           | If your code is in a file, and you're running the code from
           | the file, that's not REPL. that's interpreting or compiling a
           | file. REPL is opening interactive mode (if the language
           | supports it) and entering "2+2".
           | 
           | You calling anything else REPL is just wrong, misleading and
           | confusing.
        
             | 0x445442 wrote:
             | I think most Lispers reckon REPL development the way it was
             | described; analogous to editing code in a Smalltalk Browser
             | and Doing/Inspecting it in a Smalltalk Workspace. WRT
             | Smalltalk, you can Do/Inspect code in a Workspace and then
             | create a class and copy it over but this typically isn't
             | done that much.
        
             | dragonwriter wrote:
             | > If your code is in a file, and you're running the code
             | from the file, that's not REPL. that's interpreting or
             | compiling a file.
             | 
             | It is not "interpreting or compiling a file" if the tool
             | you are using to evaluate code in the file is sending
             | isolated blocks of code to a REPL and not, well,
             | interpreting or compiling the file.
             | 
             | > REPL is opening interactive mode (if the language
             | supports it) and entering "2+2".
             | 
             | No, a Read-Eval-Print Loop (REPL) is not exclusively used
             | by a user typing keystrokes at a terminal; like other
             | terminal software, it is a candidate for being driven by
             | other programs.
             | 
             | > You calling anything else REPL is just wrong, misleading
             | and confusing.
             | 
             | You insisting a REPL isn't a REPL when the "Read" part is
             | reading something other than keystrokes directly entered by
             | a human user is just wrong, ignorant, and confused.
        
             | mikelevins wrote:
             | 'REPL is opening interactive mode (if the language supports
             | it) and entering "2+2".'
             | 
             | If I'm going to enter "(+ 2 2)" using my normal development
             | tools, I'm probably going to start Emacs and tell it to
             | start a SLIME session with one of the Common Lisps I use.
             | When I do that, it'll create a repl buffer, because that's
             | the way I have slime configured.
             | 
             | I mean, I don't have to load slime-repl. I could just
             | interact with the Lisp's read, eval, and print functions
             | directly from an arbitrary buffer without involving the
             | SLIME repl display at all. But, y'know, force of habit.
             | Also, I do occasionally type something in SLIME's repl
             | buffer, especially if I want to see some big wad of output
             | and don't want it spewed into the middle of the expressions
             | I'm working with.
             | 
             | Most likely I won't type "(+ 2 2)" in the slime-repl
             | buffer, though. I'm more likely to type it into a scratch
             | buffer and send it to the Lisp by hitting C-x C-e. As soon
             | as I start typing Lisp code, I know I'm probably going to
             | want to add some more and maybe edit it and probably send
             | it to the Lisp again. That's just more convenient if I have
             | it sitting in a buffer in front of me, and not scrolling
             | off the top of the repl buffer into infinity.
             | 
             | So am I working in a repl? Lisp is running a loop waiting
             | for input. I'm sending expressions to it. It's reading
             | them, evaluating the s-expressions produced by READ, and
             | then printing the resulting values to a stream I can look
             | at. Sounds like a repl to me. It's what I've always
             | understood a repl to mean, including when I'm building
             | them.
             | 
             | Does it count as working in a repl when I use a keystroke
             | to send "(+ 2 2)" to the Lisp from the scratch buffer, or
             | does it only count if I actually physically type the text
             | in the buffer where the Lisp displays its prompt? What if
             | there is no prompt? What if I start Lisp and SLIME but
             | don't load the slime-repl extension? Does that mean that
             | the loop that is reading, evaluating, and printing stops
             | being a repl?
             | 
             | I'll probably save my scratch buffer to a file at some
             | point, if there starts to be enough text in it that I think
             | I might forget some of it. Does it stop being an
             | interaction with the repl the instant I save the buffer to
             | a file? I can save the slime-repl buffer to a file, too;
             | does it stop being a repl when I do?
             | 
             | What about Lisp and Smalltalk environments where the read-
             | eval-print loop doesn't display a prompt? Take a Smalltalk
             | or INTERLISP worksheet, for example. Is it a repl if
             | there's a loop reading, evaluating, and printing
             | expressions, but no prompt? Does it stop being a repl if
             | the incremental inputs and outputs get saved to a file?
             | Does it count if it's not a text file, but the environment
             | automatically saves the state of the worksheet to an image
             | file that it automatically deserializes the next time you
             | start the environment?
             | 
             | I don't think I'll adopt your lexicon, but I am sort of
             | curious where its boundaries are.
        
           | dkersten wrote:
           | Yeah, outside of simple use cases, I mainly interact with the
           | clojure REPL by evaluating forms directly from my editor.
           | It's about being able to incrementally build something while
           | having it running and updated as you go.
           | 
           | I also tend to often just reload changed namespaces and
           | resetting the (component/mount/integrant based) application
           | too. Or letting shadow-cljs live reload my clojurescript. But
           | having the REPL directly accessible is still super useful.
        
         | jmchuster wrote:
         | This is what I use as my canonical example of REPL-driven
         | development. https://vimeo.com/230220635
         | 
         | The main thing that stands out to me is playing with small
         | snippets of code in real-time as you're writing it. Contrast
         | that with writing a class and methods, then writing unit tests,
         | then running them. There might as much as ten minutes between
         | the time you start writing your code and when you run any
         | portion of it, and always running large chunks of changes.
        
           | 0x445442 wrote:
           | Having never developed code professionally in Lisp, my
           | question would be; does the REPL work flow replace test code?
           | Because if the REPL is being used as a replacement for tests,
           | then I can see future readers having a harder time groking
           | the code without the benefit of test driver code to analyze.
        
             | mikelevins wrote:
             | I'd say it's more like tests grow naturally out of repl
             | interactions.
             | 
             | Test-driven development as a discipline emerged from the
             | Smalltalk community, and I don't think that's an accident.
             | I think it's a formalization of the way that Smalltalk and
             | Lisp programmers naturally tended to work.
             | 
             | I generally start on something by making a naive model of
             | what I'm trying to accomplish and interrogating it. In
             | successive iterations, I modify the model and the
             | interrogations in the direction of what I need it to be,
             | discovering details along the way.
             | 
             | I'd say that a majority of my actual activity is testing.
             | That's one reason I prefer to interact with a repl from a
             | file: because I want a record of my interactions. I want to
             | be able to do them again and again.
             | 
             | The difference between that record and formal tests is a
             | simple matter of copying the trail of my interactions into
             | a test framework, and that, too, is how I normally work:
             | make model; test it with interactions; keep the
             | interactions around for later.
             | 
             | As I make progress, the models turn into data structures,
             | and the interactions turn into functions and tests.
        
             | reddit_clone wrote:
             | Nothing is lost. You would convert the ad-hoc test code
             | from the repl into actual unit tests in your source code.
             | With the added good feeling they have already been executed
             | successfully at least once.
        
         | chris_j wrote:
         | > What I ended up using heavily was REPL to file integration,
         | which gave me the ability to write a function normally,
         | evaluate it in the attached REPL session, and then play around
         | with it in the REPL
         | 
         | When I do REPL driven development in Clojure, that's exactly
         | what I do and that's what other folks that I know mean by REPL
         | driven development (in Clojure): define types, functions and
         | variables in a source file, often in a (comment) form, eval
         | them one by one, and copy the code out of the (comment) form
         | when it's stable enough. I wouldn't type code directly into the
         | REPL; that's not a pleasant experience in Clojure - but might
         | be pleasant in Common Lisp or Smalltalk for all I know. The
         | process that I and others use in Clojure most definitely
         | differs from the process that the OP describes and I lack
         | enough familiarity with Common Lisp to know if the process is
         | more pleasant in that language. I assume it is and I must make
         | time to learn Common Lisp properly some day.
        
           | TheAnswerMan wrote:
           | You don't even need the "REPL" buffer to do REPL style
           | development. You can just use the "eval" keybinds of your
           | editor to eval the source code in the files.
           | 
           | For any test/scratch code, just create a scratch file outside
           | your project and write the experiments there, eval'ing as you
           | type. That file could even be an "official" unit test you
           | include in the project.
        
       | tlarkworthy wrote:
       | I think the best modern example is ObservableHQ [1]. I took a
       | screen shot yesterday when I found myself interleaving runtime,
       | TDD, IDE, debugger and partial recompilation in one window. It
       | blew my mind.
       | 
       | I have never experienced such a productive programming
       | environment.
       | https://twitter.com/tomlarkworthy/status/1345321532650905601...
       | 
       | For there I can change variable at runtime. Change the
       | implementation and have the tests auto run. Correct the tests,
       | set a breakpoint in the test or implementation with "debugger;".
       | 
       | I think it might be more productive than smalltalk because of the
       | spreadsheet-like reactive recomputation. Plus it supports real
       | markup as inline documentation.
       | 
       | [1] https://observablehq.com/@tomlarkworthy/rate-estimation
        
         | mikelevins wrote:
         | Thanks for pointing it out. The site went on my reading list.
        
       | oumua_don17 wrote:
       | Writing a ray tracer in Common Lisp is an excellent video that
       | helps one understand how live editing aka REPL driven programming
       | works with a Lisp such as CL that supports it from the ground up.
       | 
       | [1] https://www.youtube.com/watch?v=N1oMRw04W3E
       | 
       | edit: fix typo
        
       | xg15 wrote:
       | Question by someone who never used interacted development before:
       | How do you save the results of your work? How do you ensure the
       | state of your program is what you think it is.
       | 
       | E.g.: Imagine you're in a breakloop as described in the article.
       | You find that local variable X is 5 when it really should be 4,
       | so you quickly set it to 4. You find function foo() is not
       | defined, so you define it. You continue and your program works.
       | Great!
       | 
       | At the end of the day, you quit your environment and shut down.
       | How do you ensure your interactive work is not lost and the
       | environment is still what you expect it to be when you start
       | again the next day. How would you compile such a program?
       | 
       | Is there some command that dumps the whole environment to a file
       | as source code? Would you save your REPL history? Would you
       | manually copy/paste relevant bits to s code file?
       | 
       | Also, if significant parts of the source code are written inside
       | the REPL, wouldn't the lack of modern IDE features be a hassle?
       | No syntax highlighting, no code completion, no code inspections
       | etc. Or are there tools that offer those?
        
         | Jtsummers wrote:
         | You can edit the source file and send it to the listener.
         | 
         | Suppose I'm in my REPL and I do this:                 > (foo
         | 'bar)
         | 
         | I get dropped into the debugger because _foo_ doesn 't exist.
         | So I switch over to my .lisp file in another buffer and I type:
         | (defun foo (symbol)         (format t "Baz ~A~%" symbol))
         | ;; this will print "Baz BAR" in the example
         | 
         | With the cursor _anywhere_ within that function definition I
         | type C-c C-c, go to the debugger and restart it. It works now.
         | 
         | My source file and my Lisp image are both synchronized.
         | 
         | In the case of changing a variable. Say I'm somewhere deep in
         | some code and this tries to run:                 ;; in context
         | x = 0       (/ y x)
         | 
         | I'm dropped into the debugger. I _know_ x was supposed to have
         | a minimum of 1. I can do a couple things:
         | 
         | 1. Examine the back trace in the debugger to see if I can
         | locate where x got an incorrect value (maybe it was the line
         | above, maybe 10 functions earlier). I make a note of what to
         | fix, maybe I fix it now.
         | 
         | 2. Change x to its proper minimum value and resume.
         | 
         | If I was able to trace how x got a wrong value, it's fixed
         | already. If not, I'll have to explore more. But I don't have to
         | restart _way_ earlier, I can just restart at this point
         | (assuming nothing else was broken by x having the wrong value
         | here) and still get the result of my program (assuming x being
         | wrong didn 't break a lot of other things). Or I can quit back
         | to the REPL and start exploring the problem.
        
         | nlitened wrote:
         | The way I do it with Clojure, you never really write any code
         | in REPL prompt directly. You write it in your source code file,
         | then send a piece of code to the REPL to update the "in-memory"
         | state of your running program.
         | 
         | So any modifications you make stay in your files, just like you
         | would normally do with any other language. But you write your
         | program while it's running, and you grow/change it piece-by-
         | piece, until it's done (by that time, your source code is
         | exactly your finished program).
         | 
         | If you want to re-start your REPL session, you just run your
         | current file in the REPL as a whole, and maybe run some "Rich
         | comments" (from the same file) to set up a limited test
         | environment (to isolate the piece of program you're working
         | on).
         | 
         | This way, you make use of syntax highlighting and all the IDE
         | features.
         | 
         | Edit: I've re-read your example with breakloop -- I don't
         | really have experience with this style of REPLing. I agree that
         | it raises a lot of questions, but thankfully functional nature
         | of Clojure discourages this kind of programming.
        
         | proverbialbunny wrote:
         | It depends what kind of environment you're using. Eg, notebooks
         | like Jupyter are a kind of REPL environment, which is how data
         | scientists typically write code. You prototype in a cell that
         | is the source code that gets saved. If you've finalized the
         | function or piece you're working on, you go to another cell.
         | 
         | The reason data scientists do it this way is load times are
         | gruesome for data science work. Sometimes it takes days to
         | process something. Imagine every time you change a line of code
         | having to wait days to see if there is a bug in code or what
         | the output is. Instead we run it in an environment that loads
         | it once and keeps it in ram (a REPL), so when working on code
         | below what is already loaded there are no load times.
         | 
         | There are other kinds of environments though. Sometimes video
         | game devs develop on a REPL where the game is running, they
         | pause it, update some code, and then go back into the game with
         | the updates automatically put in. No load times.
        
       | adamkl wrote:
       | For anyone who would like to see what REPL-driven programming
       | looks like in practice, take a look at this video here:
       | 
       | https://vimeo.com/230220635
       | 
       | The author uses the Clojure REPL to walk through the process of
       | developing some non-trivial functionality (calling an API and
       | parsing the results).
       | 
       | It's a good intro to what it looks like to interactively build a
       | program while it's running.
       | 
       | Personally, I've found that having such a tight feedback loop
       | makes development a lot more enjoyable.
        
         | irjoe wrote:
         | Interesting. I do the same thing in Elixir where I'll attach an
         | iex session to a Phoenix application so I can interrogate
         | modules and APIs as I'm building them out.
         | 
         | I'm slightly disappointed that it's already something I do day
         | to day. I had hoped that the power of the REPL wasn't
         | overstated.
        
           | adamkl wrote:
           | I think just because someone is able to do something similar
           | in another language doesn't mean that the power of a fully
           | integrated REPL is overstated.
           | 
           | Most developers are using languages where this sort of thing
           | isn't possible, and for them, experiencing a REPL driven
           | development flow can be an eye opening experience (even if
           | it's just to add it to their tool box along side more common
           | approaches like attaching debuggers and using TDD to shorten
           | the development feedback loop).
           | 
           | I don't know enough about Elixir to understand how your
           | approach is the same/different than using something like a
           | REPL with Clojure, but I did come across a pretty interesting
           | discussion on the topic:
           | 
           | https://elixirforum.com/t/what-do-you-all-think-of-
           | clojures-...
           | 
           | TL;DR - you can accomplish something similar with Elixir, but
           | the underlying technical details are different.
        
           | rozap wrote:
           | It's nice to be able to do this in production for debugging.
           | It's super powerful and one of the my favorite features of
           | erlang.
        
       | phissenschaft wrote:
       | My concept of a "REPL" is mostly defined by emacs. You would have
       | a buffer with a code file, with an active jupyter kernel with the
       | correct dependencies loaded in it. Then one would send any active
       | region with `C-c C-c` and get timely feedback.
       | 
       | With this mode https://github.com/nnicandro/emacs-jupyter one can
       | connect to a jupyter kernel running locally or remote (would
       | mostly prefer SSH port forwarding or kubectl port-forward the
       | remote jupyter server). It makes life so much easier to interact
       | with cloud environment (e.g. spark).
        
       | crabmusket wrote:
       | This reminds me of Unison's "codebase manager":
       | https://www.unisonweb.org/docs/tour#-the-big-technical-idea
       | 
       | It's not exactly a repl, but it shares similarities to the idea
       | of interacting with your source code itself via software, not
       | just by typing bytes into a file.
        
       | globular-toast wrote:
       | As someone who has used CL and Clojure to write non-trivial code
       | using REPL driven programming it makes me mad that the word REPL
       | has been hijacked to mean "interactive shell".
       | 
       | I'm thankful for this article shedding light on what it really
       | is. It's sad that most programmers haven't experienced.
        
         | lispm wrote:
         | The examples from Mikel are just not possible in a default
         | Clojure setup. It does not have break loops and it does not
         | have a dynamic object system (by design).
        
           | tluyben2 wrote:
           | Ah that answers my question I guess. Thanks
        
           | globular-toast wrote:
           | Yeah, true. I would still consider Clojure a "proper" REPL
           | driven language, though.
        
             | mikelevins wrote:
             | I do not. I have used it to write quite a bit of code, and
             | I generally like it as a language, but I don't consider it
             | a reasonable substitute for Common Lisp or Smalltalk.
             | Factor, on the other hand, is--as far as I know.
        
         | tluyben2 wrote:
         | I have some experience with clojure but besides using the repl
         | as 'interactive shell' I never tried it further (for some
         | reason I always thought it was a crippled lisp even though I
         | did some fair amount of work in it); does it offer the full
         | experience? I should try it again as that's something that's
         | actually used in real life (and has a good client side cljs
         | experience).
        
           | mikelevins wrote:
           | It does not offer the full experience. It has no breakloop
           | and no ability to browse and edit the live running
           | environment. It has no ability to rummage around inside the
           | dynamic environment of a suspended function call, much less
           | to redefine the suspended function or the types of its
           | parameters, nor to restart the suspended call. Indeed, the
           | JVM makes some of that stuff really inconvenient to do.
           | 
           | You cannot do _everything_ from the Clojure repl in the way
           | you can from a Common Lisp repl or from a Smalltalk
           | worksheet.
           | 
           | I don't know that the Clojure language design _forbids_ it;
           | you might, for example, implement Clojure on top of a Lisp or
           | Smalltalk environment and hook up their tools to Clojure
           | through its interop. That might work, and ruricolist has been
           | working on such an implementation called Cloture:
           | 
           | https://github.com/ruricolist/cloture
           | 
           | But existing Clojure implementations don't have the full set
           | of tools.
        
       | skybrian wrote:
       | It seems like the closest thing to this in widespread use is a
       | SQL database. If you want to change a table, you can run an ALTER
       | TABLE command.
       | 
       | But of course you don't want to do this in the live, production
       | database unless you're a DBA who is doing an upgrade manually for
       | some (probably bad) reason.
       | 
       | So, you can try out your ALTER TABLE commands in a scratch
       | database, but you'll need to save them to a migration script, and
       | test the migration.
       | 
       | It seems like a weakness of this sort of live updating? Sure, you
       | can modify your own running instance, but upgrading the
       | production instance(s) will often still be a lot of work.
       | Migrating a production database schema tends to be a tricky
       | thing.
        
       | irjoe wrote:
       | Is there a simple way to get code I write in a lisp REPL back
       | into my editor? That's the part missing for me and why I usually
       | only use interactive shells (REPL or otherwise) for testing APIs
       | or small pieces of code.
       | 
       | I can't imagine writing a program in its entirety in a REPL.
        
         | gumby wrote:
         | The standard technique is to run the loop inside the editor.
         | This technique dates back to the 1970s.
        
         | tarboreus wrote:
         | You write it in the editor and send statements to the REPL.
         | You're not just sitting looking at a command line. The REPL is
         | a conversation with running state, but the conversation doesn't
         | have to take place only through a single blinking terminal
         | window. In proper REPL-driven development, the editor is fully
         | integrated with the running session, and you can evaluate
         | either in an attached terminal session or through your editor.
         | Or by attaching whatever other tools to the running session.
        
           | mikelevins wrote:
           | Yes, exactly. It's perhaps my fault for not making this more
           | explicit in the essay, but the "repl" in "repl-driven
           | programming" does not mean the repl _window_.
        
             | O_H_E wrote:
             | Yeah, I suggest you expand upon that, or perhaps another
             | article showing that "conversation". Unfortunately, some
             | people - including me - have only used a repl in "one-
             | direction" and have to copy stuff back and forth.
        
               | mikelevins wrote:
               | That's a good suggestion. Walking through a set of
               | interactions is a solid idea.
               | 
               | For what it's worth, there are some videos around of
               | people actually doing it with Lisp and Smalltalk systems,
               | and pjmlp already posted a pile of them elsewhere in this
               | thread.
               | 
               | I can add a few more:
               | 
               | Kalman Reti walking through some interactions with a
               | Symbolics LispM repl:
               | 
               | https://www.youtube.com/watch?v=o4-YnLpLgtk
               | 
               | Brian Mastenbrook demonstrating Interlisp's SEDIT
               | structure editor in the Xerox Lisp environment:
               | 
               | https://www.youtube.com/watch?v=2qsmF8HHskg
               | 
               | Rainer Joswig (lispm here on HN) showing us a little bit
               | of repl and Zmacs interaction on a Symbolics Lisp
               | Machine:
               | 
               | https://www.youtube.com/watch?v=LIGt5OwkoMA&list=PLN1hNlV
               | qKB...
               | 
               | Rainer again, showing some simple interactions with
               | Macintosh Common Lisp, which was my daily driver for
               | years:
               | 
               | https://www.youtube.com/watch?v=GKG8cJl70mo
               | 
               | Ruby programmer Avdi Grimm shows some things that he
               | found cool about Pharo Smalltalk:
               | 
               | https://www.youtube.com/watch?v=HOuZyOKa91o
               | 
               | Dan Ingalls (one of the original authors of Smalltalk) in
               | a 2017 demo of Smalltalk 76:
               | 
               | https://www.youtube.com/watch?v=NqKyHEJe9_w
               | 
               | There are some other things I'd like to find for lists
               | like this, but haven't been able to. In particular, a
               | good demo of Apple's SK8 would be great.
               | 
               | If you can imagine a full-color Hypercard that could
               | crack open and reprogram absolutely everything on the
               | screen, including the machine code that drew the window
               | system's widgets, all in a repl while the code was live;
               | in which you could grab an arbitrary widget and drop it
               | on the repl window to get a live variable reference to
               | the widget, and then inspect it, operate on it, and
               | reprogram it, again, while everything continued to run;
               | in which you could build new window-system widgets by
               | snapping together shapes and telling them to become
               | widgets; in which you were not limited to HyperTalk for
               | coding and text strings for data, but had a full Common
               | Lisp at your disposal plus a Minsky-style frame system
               | for representing data and knowledge, then you have some
               | idea of what SK8 was like.
        
       | lebuffon wrote:
       | Nobody has mentioned low-level programming in a REPL. Not as
       | common to be sure. Forth works this way and even has REPL
       | Assembly Language. It's been there for over 40 years.
       | 
       | Testing Forth and ASM code snippets in the REPL before committing
       | to them helps eliminate those nasty assumptions about what
       | "should" work.
        
         | mikelevins wrote:
         | Agreed, FORTH is repl-driven in the sense I mean. The main
         | difference from Lisp and Smalltalk systems is that FORTH
         | environments are, generally speaking, more spartan.
         | 
         | In the late 1980s I had a group of friends at Apple that
         | included Smalltalk, Lisp, and FORTH programmers. We certainly
         | found plenty of things to admire and attempt to steal from one
         | another, and everyone accepted the basic goodness of building
         | systems by engaging in conversation with them as they run.
         | 
         | Lisp and Smalltalk cross-pollenated each other more than each
         | did with FORTH, but maybe Slava Pestov's Factor is a glimpse of
         | what you get if FORTH is more in the mix.
        
           | 0x445442 wrote:
           | Yeah, Factor's environment looks really similar to something
           | like Smalltalk. The thing with Forth is that it looks really
           | daunting to perform higher level tasks that I'd be interested
           | in performing on a day to day basis.
        
             | mikelevins wrote:
             | It can definitely be done, but historically, that's not
             | really been its sweet spot.
        
       | tluyben2 wrote:
       | I think it would help as well to list languages that have various
       | levels of 'real repl' implementations. Wonder what modern
       | (systems you can 'make money with') there are that support this.
       | I know common lisp + smalltalk and worked with both and really
       | liked them for these reasons. I miss this functionality all the
       | time as it was far more efficient (to me!) than modern debuggers.
        
         | rightbyte wrote:
         | Isn't Python and Matlab a 'real' REPL? They are money makers.
        
           | mettamage wrote:
           | The article specifically stated that Python does not have a
           | real Read-Eval-Print-Loop mechanism like Lisp or Smalltalk.
           | For example, the Python REPL doesn't go into a breakloop
           | mechanism for undefined functions.
        
           | mikelevins wrote:
           | It's not about whether they make money. It's about whether
           | you can do absolutely everything needed to build your program
           | interactively by talking to it while it's while it's running.
        
           | coldtea wrote:
           | The article describes specific capabilities (full
           | program/system dynamic redefinition) afforded by a real or
           | full REPL.
           | 
           | Python doesn't have that, and it's mentioned explicitly as
           | not having that.
           | 
           | It's not about "existing in the real world" or "real enough
           | to make money with".
        
       | mvc wrote:
       | Definitions matter. A repl is a read, eval, print, loop.
       | 
       | That means the code you enter at the prompt is converted from a
       | String into literal data that can be evaluated (read). The data
       | is then evaluated to produce a result (eval), and the result is
       | then printed (print).
       | 
       | If that's not what your REPL is doing then it's not a repl.
       | 
       | That is not what's going on in e.g. python or ruby "repls". There
       | is no "read" step here converting the text of the program to
       | data. The program text is simply passed to an "eval" function
       | that produces the result directly.
        
         | TazeTSchnitzel wrote:
         | This seems extremely pedantic. Python and Ruby will "read" the
         | string inside the "eval" function.
        
           | mikelevins wrote:
           | Other people are making the same point I'm about to make, but
           | I'm going to try to clarify it anyway because, y'know,
           | besides being a programmer, I'm also a technical writer, and
           | I just have to scratch that itch.
           | 
           | Common Lisp source code (and the source code of its immediate
           | ancestors) is not made of text strings. It's made of
           | S-expressions, which are made of cons cells, symbols,
           | numbers, and so on.
           | 
           | A text file of "Lisp source code" does not actually contain
           | Lisp source code. It contains a text-based serialization of
           | Lisp source code. Other serializations are possible (and
           | there are things you can do in a Common Lisp repl to see some
           | of them).
           | 
           | The "read" in "read-eval-print" means "deserialize the text
           | into the source data that it's meant to represent".
           | 
           | This point is not trivial pedantry because the full power of
           | the Lisp language is available to the read process, and can
           | be brought to bear on how reading is done and what happens
           | when you do it. Compilers for other languages certainly do
           | read text strings and convert them into tree structures and
           | so forth, but the difference is that those data structures
           | are private to the compiler; the data structures that Lisp
           | reads into are standard parts of Lisp's public API, as are
           | the read function, the compile function, the eval function,
           | the print function, and so on. It's all on the table for you
           | to work with.
           | 
           | The same is true of the disposition of the s-expressions
           | produced by the read process; you have an opportunity to
           | bring the whole of the Lisp language to bear on those
           | s-expressions before they are ever passed to (compile or)
           | eval. Then, once again, what eval produces is S-expressions,
           | and those, not strings, are passed to the print function. You
           | once again have the opportunity to intervene in the process
           | that produces the text serialization.
           | 
           | It so happens that I've spent the past six months working on
           | an AI machine-control system written in Common Lisp, and
           | every one of these capabilities was an important part of the
           | work we were doing.
        
           | lispm wrote:
           | In Lisp it makes a difference, because source code is data
           | (other than text) and one can also compute source code in the
           | REPL.
           | 
           | For example we can write a macro in Lisp and play around with
           | it giving it code as data and see the result as code as data.
           | CL-USER 1 > (defmacro while (condition &body body)
           | `(tagbody start                                 (if (not
           | ,condition) (go end))                                 ,@body
           | (go start)                                 end))
           | WHILE              CL-USER 2 > (setf a 1)         1
           | CL-USER 3 > '(while (< a 4) (print a) (incf a))
           | (WHILE (< A 4) (PRINT A) (INCF A))              CL-USER 4 >
           | (macroexpand-1 *)         (TAGBODY START (IF (NOT (< A 4))
           | (GO END)) (PRINT A) (INCF A) (GO START) END)         T
           | CL-USER 5 > (pprint *)              (TAGBODY          START
           | (IF (NOT (< A 4)) (GO END))                  (PRINT A)
           | (INCF A)                  (GO START)          END)
           | CL-USER 6 > (eval ***)              1          2          3
           | NIL
        
             | kitd wrote:
             | All very nice, but in Python/Ruby/etc, the source code
             | isn't data. So your point is only relevant to Lisp, not
             | REPLs generally.
        
               | lispm wrote:
               | That's why it is not a REPL (Read Eval Print Loop), but a
               | ReadString, Parse, Compile, Execute, Loop.
        
           | [deleted]
        
       | boxmonster wrote:
       | I've been doing debugger driven development my entire adult life
       | and consider it a bad habit. I'm much better off and happier when
       | I plan out and compile in my head. Then I get a good feeling when
       | it compiles and runs the first time. If not, then I need to think
       | about it some more, not sit in the debugger trying things out.
        
       | mikewarot wrote:
       | I had no idea this was possible now, and to find it was
       | commonplace for decades makes it even more amazing, and worrying
       | what else I've missed. Thanks for sharing this here!
        
         | pjmlp wrote:
         | Be prepared to be amazed with what Xerox PARC and others were
         | doing, while Bell Labs was busy pushing for UNIX.
         | 
         | "Eric Bier Demonstrates Cedar"
         | 
         | https://www.youtube.com/watch?v=z_dt7NG38V4
         | 
         | "Emulating a Xerox Star (8010) Information System Running the
         | Xerox Development Environment (XDE) 5.0"
         | 
         | https://www.youtube.com/watch?v=HP4hRUEIuxo
         | 
         | "Documents as User Interfaces Video Demo"
         | 
         | https://www.youtube.com/watch?v=0-_zVkrWCOk
         | 
         | "SYMBOLICS S-PACKAGES 3D GRAPHICS AND ANIMATION DEMO"
         | 
         | https://www.youtube.com/watch?v=gV5obrYaogU
         | 
         | "Alto System Project: Dan Ingalls demonstrates Smalltalk"
         | 
         | https://www.youtube.com/watch?v=uknEhXyZgsg
         | 
         | "Action!, the worlds first dynamic interface builder - 1988"
         | (Interface Builder percursor, written in Lisp)
         | 
         | https://vimeo.com/62618532
         | 
         | "The Interlisp programming environment"
         | 
         | http://larry.masinter.net/interlisp-ieee.pdf
         | 
         | And the cherry, how Lucid used Lisp ideas for their Energize
         | C++ IDE, including an image based format for AST storage
         | 
         | https://www.youtube.com/watch?v=pQQTScuApWk
         | 
         | https://www.dreamsongs.com/Cadillac.html
        
           | hcarvalhoalves wrote:
           | Watching the Symbolics demo nowadays, it's amazing what they
           | had back then.
        
           | vblxt wrote:
           | These lock-in IDE environments, while impressive, thankfully
           | never got really popular.
           | 
           | I find superior languages like Lisp or Standard ML most
           | enjoyable in text files.
        
             | pjmlp wrote:
             | Look better around you.
             | 
             | - macOS, Windows, Android, ChromeOS
             | 
             | - InteliJ, Visual Studio, XCode/Playgrounds
        
       | ncfausti wrote:
       | For people who use Clojure (or another REPL-capable language),
       | would you say that this is the main reason why you use it?
       | 
       | Is it common to write Clojure or other REPL-capable languages in
       | a more "traditional" manner (like how one would write say, Java
       | or C)?
        
         | tincholio wrote:
         | I'm not sure I'd say the REPL is "the main" reason I use
         | Clojure (there are many reasons, really), but it is an
         | important part of the experience.
         | 
         | I don't know of anyone who is experienced in Clojure and works
         | in a "write-compile-test" way like you'd do in C or Java. While
         | it's certainly feasible, it's not how you're supposed to do it.
        
       | psykotic wrote:
       | He's right to emphasize the properties of the systems that
       | support interactive development with a long-running image. In
       | Common Lisp, the difference between defvar and defparameter is a
       | simple example. Traditional Smalltalk systems only supported
       | image-based development. But I always preferred the moderate
       | approach exemplified by most Lisp systems where the source code
       | isn't overly entangled with the image state.
       | 
       | As a long-time but now lapsed Lisper, I never understood some
       | people's elevation of the REPL. A command-line REPL is a poor
       | man's development environment. If you're doing interactive
       | development in Lisp, you'll be far more productive if you use a
       | normal buffer with eval-defun, eval-buffer and friends. When
       | debugging, you'll primarily want auto-updating watch expressions
       | and object inspectors. And when you do have good use for a REPL,
       | you'll still want it to exist within a proper buffer with
       | persistent history, inline object inspection, etc, instead of the
       | low-effort rlwrap terminal experience which usually passes for a
       | REPL.
       | 
       | All of this holds doubly true for Smalltalk environments.
        
         | mumblemumble wrote:
         | Why not all of it?
         | 
         | Long ago, I was doing Objective-C development with an embedded
         | F-script REPL, hot code swapping, and XCode's built-in
         | debugger. It was very nearly as nice as Smalltalk in many ways,
         | only without the whole IBD situation.
        
           | moocowtruck wrote:
           | that doesn't sound nearly as nice as smalltalk
        
         | lispm wrote:
         | The mindset is slightly different. From a buffer one can
         | interact with the system and it may show things inline.
         | 
         | The REPL, often called a 'Listener' in Lisp is an explicit
         | dialog with the system, where the dialog is visible and parts
         | of the dialog can be reused and inspected. Also the dialog can
         | be suspended temporarily and we interact with the running code
         | itself in break loops until we resume the original dialog in
         | some way.
         | 
         | This usually is the horror for people wanting predictable code
         | - where in an interactive Lisp, one can change code
         | interactively at runtime.
        
         | mikelevins wrote:
         | You misunderstand me. By "repl-drive programming" I do not mean
         | programming that is fixated on the "command-line repl". On the
         | contrary, what I'm talking about has much more in common with
         | what you describe as "far more productive". It's not about a
         | particular shell or window or buffer; it's about a runtime
         | environment that is designed to support writing software by
         | building and changing it as it runs.
         | 
         | By "repl", I do not mean the window or the buffer or the shell
         | program. I mean the loop of: read an expression, evaluate it,
         | and present the results, in the context of a runtime that is
         | designed to comprehensively support it.
         | 
         | Moreover, you can have all of the tools that you listed and
         | still not be working with a properly-designed repl-driven
         | environment. For example, I built a 3D interactive environment
         | on the JVM that worked quite well--it launched into the 3D
         | environment ns no more than a second or two, and could build
         | whole procedurally-generated scenes in a few seconds. It
         | supported networked multiuser interactions. I could start it
         | from a repl and dynamically alter scenes and objects and their
         | behavior in the environment by talking to them.
         | 
         | It still wasn't a proper repl-driven environment because the
         | underlying runtime could not correctly handle dynamic
         | redefinition of classes and methods. That meant that if I
         | decided that I needed to change a representation or something,
         | I had to kill the environment and rebuild it. It meant that
         | there was always a gratuitous barrier that I might run into at
         | any moment. It meant that some abitrary set of things I was
         | working on was always on the other side of that barrier.
         | 
         | Contrast that to working with, for example, SK8, where I could
         | redefine absolutely everything in the environment (including,
         | for example, the system-level procedures used to draw window
         | frames) without ever restarting the code that was under
         | development.
        
       ___________________________________________________________________
       (page generated 2021-01-04 23:01 UTC)