What makes a repl?
This is an episode of Thoughts on Functional Programming, a podcast by Eric Normand.
There's a lot of discussion on Twitter about whether Node has a repl or Python has a repl. Do they have repls? How can we tell? Well, my opinion is that what's important is how it's used, not a set of features.
Eric Normand: What makes a REPL a REPL? Does your language have a REPL? Does language X have a REPL?
My name is Eric Normand. I help people thrive with functional programming. There's a lot of debate on Twitter right now, about whether Node has a REPL or Python has a REPL.
I see a lot of Lispers, Clojurists, common Lispers, schemers, replying and telling them, "No, it's not really a REPL." There's a lot of outcry from both sides. Are these Lispers being smug like they always are? Do they have a point? Remember, they can't explain themselves.
I use the Python prompt all the time. Isn't that a REPL? What makes a REPL anyway? I don't want to get into semantics. I don't want to talk about what makes a REPL. It's not about a list of features like, if a REPL can do X, Y, Z, if it can reload code in a running server, or whether it's got a prompt or a printer.
I don't want to go into those details. It might be important, but it doesn't interest me. They can hash that out and other people can. What I want to talk about is the qualitative difference in the workflow between languages with...
Let me just be very, very clear between Lisps [laughs] and everything else. There might be other languages with good REPLs. I'm not trying to be so strict that it's only Lisps have good REPLs. I'm not familiar with the other languages. You could say Fourth has a REPL. I don't know. I've never done it. I've never seen someone program in fourth, so I can't really say.
Haven't done much Ruby, but I've seen it. I can generalize about it. What is the difference? That's what I want to get into. I don't want to mince words. Not saying that X, Y, Z language that I've never used doesn't have a REPL.
What I want to talk about is this qualitative difference in the workflow from what I've seen in some languages versus what's very common in Lisp. That's what I'm trying to talk about. Here's the difference. When you're programming in Lisp, you've got enough experience. You have to be good enough at Lisp.
People execute code in the REPL continuously. I'm talking about if they're deep in it, they're executing their code like every 10 seconds. They type something in, execute. Type something in, execute. Type something in, execute. It's a very, very interactive experience of programming in Lisp.
This is what we call REPL-driven Development. To give it a name, that you're very involved in this running system, or you're writing some code, and then executing it and seeing what it does, and doing a little experiment. Does my code even compile right now?
It's very similar to what I've seen in the Ruby community because they're really good at this is TDD, Test-driven Development, where you make a change, you run your tests. You make a change, you run your tests. You make a change, you run your tests.
The difference is the tests are this giant suite of tests. Then the REPL, it is a live system with all your code loaded and you're now compiling or executing one small piece again or a large piece, depending. You can execute different sizes and things. It's not running tests. It is executing whatever code you want to execute.
Usually, it's what you're working on. You're editing a function, recompile the function. Editing a function, recompile the function. Did I do what I need to do? Let me test this function out myself manually. Let me add this a little bit to the function and compile it.
There's a lot of stuff going on back and forth between you and then the REPL. What I think the difference is, I can't boil it down to features. If you had this, then it's a REPL, and REPL-driven development is possible.
What I can say is that, Lisp REPLs are designed for that. They are designed for this back and forth continuously, like 10 second increments of feedback. They've been optimized for that, for the past 60 years. That's the difference. It's not this feature or that feature. It's that any sharp edges, any obstacles have been removed.
As an example, in Clojure, Clojure was designed to execute one expression at a time. If you're in a file, the first expression gets executed, then the second one, then the next one, then the next one, then the next one, which is very similar to me typing in the expression, send it to the REPL. Type in the next expression, sent it to...
It goes through the same code paths. Your production code is running the same way as if you were typing it in at a REPL. It's not like this thing bolted on, where we make a little loop that has a prompt that prints a prompt, and then can execute a thing so that you can try something out every now and then. It is designed that way.
The difference is that, when I'm using Python, and I had to code something in Python, I will often be coding and say, "How does this thing work?" I go to the terminal. I launch the Python and I type a thing or two in. Sometimes that answers my question.
I'm done and I close Python and I go back to my code. This is too slow. It's too infrequent. It's a qualitative difference that once you're doing this very often, and things are just working, and you're sending back and forth, and you're getting lots of information about how your code is running all the time, it's a totally qualitative difference.
I just made a change in my file. How do I get it into the REPL? I don't even mind copy pasting or whatever, but this file was required by this other file. How do I get the changes to propagate through the required statement? It's not clear how to do that.
Whereas, in Clojure, that's the default. Code executes in a namespace. You run this code in the namespace. This code replaces the old code, the old function. It's just the way it works. It's part of the semantics. It's not a trick. It's not software that will figure that out for you. It's just you just do it, and it works.
What I'm saying is, it's not that it's impossible. It's that just hasn't been streamlined. I'd love to see someone write out. "Here's how you do it in Python. It is that easy. Eric, you're wrong." I'd love to say and see someone do that. Please, if that's you, if you know how to do this, come on. Bring it on.
Let's get REPL-driven development going in Python. I haven't seen it. I've tried and I couldn't do it. That's what I think is important about REPLs. It's not a list of features. There is no like bar to get over. It's more of a qualitative difference in workflow and usage. Lispers have always been doing this since Lisp was invented.
There's a lot of history behind that. I don't want to go into that right now. Lispers have always been about fast feedback with the running program. Other languages aren't designed that way. They just aren't. They're designed to be like given to an interpreter, like a file given to an interpreter, not an individual expression run at the prompt.
I think that little design decision, that little change in the outlook and the design does have a big effect down the line when you're talking about a developer in a workflow. That's the difference.
Please go to lispcast.com/podcast if you want to find the past episodes, links to subscribe. You can get audio, video, and text versions. You can find ways of contacting me. I'd love to hear that you figured out how to do REPL-driven development in Node, for instance. That'd be awesome or in Python or Ruby. That'd be awesome. I'd love it.
I'd share with everyone I know because I love REPL-driven development. It's amazing. I'm waiting for you. See you later.