loading...
Cover image for Moving faster with REPL

Moving faster with REPL

joncassdev profile image Jonathan Cass ・4 min read

Introduction to REPL

Developers like to "move fast and break things." Well, we like to move fast anyway. A "REPL" is a tool I've found that prevents me from getting bogged down working within the context of a large application. Of course, at some point, my new feature or bugfix has to be integrated into the codebase, but starting there adds friction and slows me down. In this post, you'll learn about what REPLs are, and how to use them to work efficiently.

A REPL is a Read-Evaluate-Print Loop. This concept was first introduced in the Lisp programming language, to allow quick experiments in Lisp. In Lisp, the following is an implementation of a basic REPL:

(loop (print (eval (read))))

Reading the commands from the inside out (which is how they are executed in Lisp), you can see where REPL gets its name!

Generally speaking, you invoke a REPL from the command line. When you start up a REPL, it will take you to a new interface, similar to a command line, but your instructions are interpreted in the language of the REPL. In fact, you can think of a command prompt as a REPL for Bash. Once in a REPL, you can run commands, define variables, write functions, etc. and see the results.

Examples: Python and Node

Both Python and Node come with fairly sophisticated REPLs when you install them. Here are some examples you can try!

Python

Start up the Python REPL, by typing python3 at your command prompt (in this case, user@comp ~$) and pressing enter. It will print out some information on your python installation, and leave you at the REPL prompt (the >>>):

user@comp ~$ python3
Python 3.6.1 (default, Apr  4 2017, 09:36:47) 
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>

From here, you can perform computations, define variables and functions, etc:

>>> 1+1
2
>>> greeting = 'hello world'
>>> print(greeting)
hello world
>>> def print_greeting(greeting):
...     print(greeting)
... 
>>> print_greeting('hello world')
hello world
>>> print_greeting('hello function')
hello function

Exit with ^d (ctrl+d)

>>> ^d
user@comp ~$

Node

Enter the Node REPL

user@comp ~$ node
> 

Just like in Python, you can perform computations, define variables and functions, etc:

> 1+1
2
> const greeting = 'hello world';
undefined
> console.log(greeting);
hello world
undefined
> const printGreeting = (greeting) => console.log(greeting);
undefined
> printGreeting('hello world');
hello world
undefined
> printGreeting('hello function');
hello function
undefined

Exit with ^d (ctrl+d)

> ^d
user@comp ~$

The undefined that crops up in the Node example is the return value of each statement. If your statement has a defined return value, that will be printed instead, as in the 1+1 example. Note also that these REPLs have command history, so you can press “up” to view past commands, even between sessions.

Implementation

The REPL is just like any program that you run from your command line. When you run it, it gives some output and then waits for user input. When a statement is entered, it evaluates the statement, and prints out the result. In both Python and Node, there are built-in modules that can provide a REPL with some injected “context”. You can read @rpalo ’s excellent post here for how to make use of the Python library Code to create your own REPL. Here’s how to do it in Node:

// include the repl library
const repl = require('repl');

// start it up, with the desired prompt string
const r = repl.start('> ');

// inject a couple pieces of context
r.context.text = 'This is some text';
r.context.greet = (name) => console.log(`hello ${name}`);

We can save this as my_repl.js and then start it up and use it as follows (note that text and greet are already defined for you because they were injected into the context):

user@comp ~$ node my_repl.js 
>
> 1+1
2
> text
'This is some text'
> greet('Jon')
hello Jon
undefined
> const greetAndCompliment = (name) => {
...   greet(name);
...   console.log('nice code');
... }
undefined
> greetAndCompliment('Jon')
hello Jon
nice code
undefined
> 

Everyday Uses

I find REPLs most useful when trying out a simple experiment. For example, instead of creating a test.py script to confirm that default parameters work the way I think they do, I can just fire up the REPL and confirm it:

>>> def print_greeting(greeting='hello world'):
...     print(greeting)
... 
>>> print_greeting()
hello world
>>> print_greeting('hello overridden default')
hello overridden default

Wrap Up

Now that you’ve learned about REPLs, you might also be interested in unit tests, and Test-Driven Development, or TDD. These increase developer velocity in a similar way, by shortening cycle time. They have the added advantage of increasing code quality. For further reading and REPLing, check out the Wikipedia page or repl.it.

Thanks for reading!

Posted on by:

joncassdev profile

Jonathan Cass

@joncassdev

Software Engineer at Google. Former Architect at athenahealth. I love teaching, learning, and coding! Opinions my own.

Discussion

markdown guide
 

I always thought of Python having more of an interactive shell, rather than a REPL.

The REPL in Lisp also has the ability to inspect, debug, edit, on live code. The REPL is like having a built-in IDE, at the caliber of PyCharm.

Python distinguishes code from data, whereas Lisp does not. Python is not homoiconic, which Lisp's REPL leverages heavily.

Python print(repr(eval(raw_input("> ")))) compared to Lisp (loop (print (eval (read)))) has several other important differences. Python's eval works on only expressions. Python's eval combines parsing and evaluation, which leads to perennial Python problems of "safe eval" and "string representation of a dictionary" (leading to ast.literal_eval). Lisp's REPL is core to the engine, so a developer can extend Lisp at the most fundamental level (and has access via the REPL to the core engine; the developer can change the syntax of Lisp itself, such as by using W-expressions or L-expressions instead of only S-expression), whereas Python's interactive shell does not provide that kind of extensibility.

That being said, I'm a Python fanboy, and I don't much care to program in Lisp. Your mileage may vary.

 

Thanks for your thoughts!

Yeah, to be honest I actually haven't used Lisp, and it's definitely not a perfect analogy. I think that for simple use cases, the various flavors of shell, REPL, etc are somewhat interchangeable, but it is an important distinction as things get more complex.