A REPL for Fat-Finger Friendly Typing


From Scratch Code

Helping you master Rust and
Python, from scratch.

My Python interpreter, Memphis, has a REPL (read-eval-print loop)!

This is old news. As long as you made zero mistakes while interacting with the wise old owl 🦉, you could interpret to your heart’s content. Assuming you never wanted to evaluate the same statement twice, or if you did, didn’t mind retyping it. Also with zero mistakes.

I was perfectly content with this REPL. Thrilled even. I had written home about this REPL. But my bosses’s bosses’ bossi demanded we improve the REPL for the bottom line for the people. They called me into their office and nodded me into the chair across from their mahogany desk. “Some users are expecting the backspace key to work.” TO HELL WITH THE USERS! “The up arrow should bring up their last command.” ARROW KEY SUPPORT IS SO NINETIES! “Can you have this done by the end of Q5?” I QUIT!

So I went back to my desk and improved the REPL.

I improved it so much that all the keys worked. The backspace key, the up arrow, the down arrow, the left arrow, and last, but not least, the backspace arrow. An accountant could have a field day with the new REPL. tick tick tick tick tick tick tick tick tick. That’s the accountant typing numbers, not a bomb slowly diffusing.

I sent the REPL down to the lab and told my main machinist to put a rush job on this order. It’s the REPL I said and from the look in their eyes I could tell they understood. 750ms later the build was completeand we had arrow key support. I took the product back to the big wigs, begged for my job back, and asked them what they thought. They ran a few commands, printed some prints, and added some adds. They made a mistake and hit the backspace key. I rolled my eyes because seriously who makes mistakes but they seemed satisfied. They realized they didn’t want to run a long command they had already typed out and this is where my life went to hell in a handbasket. They. hit. Ctrl. C. Seriously, who does that?! You know that ends the current process, right? RIGHT???

“We need Ctrl-C support by the end of next year.” These people and their demands. I would add Ctrl-C support. But it absolutely would not be within the next two years.

So I went back to my desk and added Ctrl-C support.

What made this REPL worthy of people with fat-fingered tendencies?

Would I be a tool?

I have staked my entire professional and financial future on building things “from scratch,” so I faced a quandary on day 1 of this project. I chose to use crossterm for the key detection primarily because of the cross-platform support. Honestly though, crossterm was very, very good. The API is intuitive and I was especially pleased with KeyModifiers (which we needed to handle Ctrl-C, which I thought was unnecessary, see above).

Raw mode is a pain

We needed it so that the terminal wouldn't handle special keys for us. But damn, I didn't realize it would turn our screen into a malfunctioning typewriter. Anyway, I had to normalize all strings to add a carriage return before any newline characters. Which worked fine and I'm THRILLED about it.

Integration testing was fun

Under my old REPL (which I preferred, see above), I could test it integration-ally by just running the binary and passing in some Python code to stdin. That stopped working when using crossterm I think because of a contract dispute. I honestly can’t explain it fully, but event::read() would timeout and fail in the integration test provided with stdin input. So I mocked it.

Which resulted in the whole thing becoming a unit test? Honestly I don’t know. At this point, I call it an integration test if I either a) call a binary inside another binary, or 2) launch a server / open a port / listen on a socket inside a test. If you have another definition you’d like to leave in the comments, please don’t because that sounds annoying TBH.

We can now test these common scenarios with fairly little boilerplate.

Code entrypoints get me out of bed in the morning

One of my motivations in adding a REPL at all was because I believe you make your code better when you add a second entrypoint. You are essentially becoming the second user for your library, which helps you get closer to understanding The One Perfect Abstraction we are all poking at our keyboards in search of. I mean this point earnestly.

Want to build your own HTTP server from scratch in Rust?

“Zero Dependencies” 😉

The REPL is now behind a feature flag as a way to get back at management. I am keeping alive the ability to interpret Python code with the help of zero third-party crates, which means crossterm would either need to be an exception or I would introduce a feature flag. Now, if you compile without the REPL enabled and run “memphis”, it will politely tell you “wrong build, dumbass.”

Goodbye

The REPL is here. You can run it like this. If you want to buy it, that sounds like a scam. Be well & Talk soon.

Click here to view this post in your browser, copy the code snippets, and leave a comment.

Elsewhere

In addition to mentoring software engineers, I also write about my experience navigating self-employment and late-diagnosed autism. Less code and the same number of jokes.

Lake-Effect Coffee, Chapter 1

A fictional look at a real goal of caffeine-fueled connection.

From Scratch Code
Unlock the power of Rust and Python by mastering advanced concepts to build code that demonstrates your expertise and creativity. Offering mentorship and courses to help you create complex libraries, systems, and real-world tools from scratch—all in a fun and supportive environment.

Website | Blog | Contact

Forwarded this by a friend? Subscribe here.

From Scratch Enterprises LLC
418 Broadway, Ste N, Albany, NY 12207
Unsubscribe · Preferences

From Scratch Code

Unlock the power of Rust and Python by mastering advanced concepts to build code that demonstrates your expertise and creativity. Offering mentorship and courses to help you create complex libraries, systems, and real-world tools from scratch—all in a supportive and sometimes silly environment.

Read more from From Scratch Code

From Scratch Code Helping you master Rust and Python, from scratch. Lifetimes are a fascinating feature of Rust and the human experience. This is a technical blog, so let’s focus on the former. I was admittedly a slow adopter for leveraging lifetimes to safely borrow data in Rust. In the treewalk implementation of Memphis, my Python interpreter written in Rust, I hardly leverage lifetimes (by cloning incessantly) and I repeatedly elude the borrow checker (by using interior mutability, also...

From Scratch Code Helping you master Rust and Python, from scratch. A few months into development, I decided my north star for Memphis would be to run a Flask server entirely within my interpreter. I had no idea how much work this would entail, only that it sounded cool and would probably teach me a lot along the way. If I were making this goal today, I may pick FastAPI or nothing at all because that was silly of me. Python stdlib A big decision I encountered was how to deal with the Python...

From Scratch Code Helping you master Rust and Python, from scratch. I’m currently exploring two interesting topics for Memphis, my Python interpreter in Rust: building for WebAssembly and embedding CPython. With no major milestones to report this week, I thought I’d share some in-progress thoughts. For me, Memphis is been a project for expanding my conceptual understanding through practical experiments—hopefully, this post can do the same for you as we walk through some of the design...