PurelyFunctional.tv Newsletter 393: always start with a repl
From OO to Clojure Workshop!
Watch my free workshop to help you learn Clojure faster and shift your paradigm to functional.
Clojure Tip 💡
always start with a REPL
Whenever I'm programming in Clojure, the first thing I do is start a REPL. There are a few reasons.
- It takes time to start and I'm going to use it anyway.
- The REPL is required for a lot of the "IDE" functionality I use (such as online documentation).
- I don't want to be deep in thought and have to wait for it to start.
- I'm lost without the constant feedback.
I really do feel lost when I don't have a REPL. I can write code without a REPL. But as the lines of code that I write that haven't been run at a REPL pile up, I start to feel a bit anxious. I'm no longer confident that they work correctly. And that anxiety can wreck flow.
Now, you might think that something like more static checks would help regain my confidence. But if you did, you would be mistaken. Static checks are great, but they only give you confidence that you aren't going to mess up production with bad code. They can't give you confidence that the code you've written but haven't run through the compiler will type check on the first try.
Now, I'm not anti-types. I think types are great. This is just not something they can solve by themselves. What you really want with types is continuous type checking. The worst thing is to write 100 lines of code, run the type checker, and learn that you have type errors somewhere you now have to find. The problem isn't that they are hard to find, it's that it takes time to go back and fix them. You are bumped out of flow and by the time you're done, you've forgotten what you were even trying to do.
It's much better to write 1 line of code and find out there's an error. You can fix the line you just wrote, while it's still in your working memory. This is the same confidence that we get when doing RDD (REPL-Driven Development). You write a small piece of code, run it, and confirm the output. It's all about the constant feedback.
The other tip I want to give related to this one is that if for some reason your REPL isn't working, it's worth it to stop whatever work you are doing and fix your REPL. It may feel like yak shaving---unimportant work that is just in the way of the real work---but after years of working in an RDD workflow, I know how valuable the REPL is. I work so much faster at the REPL that it is worth days of investment to get the REPL up and running again.
How is that possible? Flow is an amazing state that our brains can get into. If we can enter it (remember: 1. clear goals + progress, 2. clear
- immediate feedback, 3. match skill with challenge), our brains work more efficiently. Instead of swapping lots of things in and out of working memory, we can hold everything we need right inside. Just like in a CPU, cache misses are the worst. If everything we need is in the cache, we can work at maximum speed.
The REPL gives you all three requirements for flow. It gives you a sense of progress, immediate feedback, and balanced challenge. It helps you get into flow.
I said that the REPL's speedup is worth days of setting up. It usually doesn't take that long to fix a broken REPL. But in extreme cases, it could. Can flow really account for that much productivity? I'm not sure. But here's another way to look at it. If you write a line of code and it turns out to not work correctly, how long does it take to find and correct the problem? What if you write 10 lines of code? How long does it take to find and correct the problem? What about 100 lines? What about 1000 lines?
My conjecture is that the time it takes to locate the problem, fix it, and verify that it is fixed is not proportional to the number of lines. I think it's at least quadratic. I can't prove it. It's just a sense I get. If it were proportional, you would be better off writing lots of code and testing it all in one go---that would save time. If it is quadratic, the smaller the piece of code, the more time saved. I believe it is (at least) quadratic, so there's lots of time saved by testing every line as I go.
That's how it is worth it to spend the afternoon getting my REPL working again. It lets me get into flow (more efficient brain) and reduce time searching for bugs.
Quarantine update 😷
I know a lot of people are going through tougher times than I am. If you, for any reason, can't afford my courses, and you think the courses will help you, please hit reply and I will set you up. It's a small gesture I can make, but it might help.
I don't want to shame you or anybody that we should be using this time to work on our skills. The number one priority is your health and safety. I know I haven't been able to work very much, let alone learn some new skill. But if learning Clojure is important to you, and you can't afford it, just hit reply and I'll set you up. Keeping busy can keep us sane.
Also, if you just want to subscribe for a paid membership, I have opened them back up for the moment. Register here.
Stay healthy. Wash your hands. Stay at home. Wear a mask. Take care of loved ones.
Clojure Challenge 🤔
Last week's challenge
I was out of town last week (with a pre-scheduled newsletter) so there are two weeks of challenge submissions I need to share.
- Powers in range - submissions
- Binary search - submissions
Please do participate in the discussion at the submission links above. It's active and it's a great way to get comments on your code.
This week's challenge
Better filename sort
Typically, strings are sorted by their unicode value. That means you get some weird behavior. We see this with naive sorts of filenames.
- "Zoo.txt" is sorted before "academy.zip"
- "12 Final chapter.txt" is sorted before "2 Second chapter.txt"
We should be able to find something a bit smarter. Let's try two rules:
- Case insensitivity --- a and A should be equivalent
- Numbers in order --- strings of digits should sort in numerical order, not string order
Write a function that you can pass to
sort-by that will sort filenames
(sort-by filename-order ["Zoo.txt" "academy.zip"]) ;=> ("academy.zip" "Zoo.txt") (sort-by filename-order ["12 Final chapter.txt" "2 Second chapter.txt"]) ;=> ("2 Second chapter.txt" "12 Final chapter.txt")
It's also unfortunate that "elephant.txt" is sorted so far away from "éléphant.txt" (the French version). Make them sort next to each other. "e" should come before "é".
You can also find these same instructions here. That's also where submissions will be posted. And there's a great discussion!
As usual, please reply to this email and let me know what you tried. I'll collect them up and share them in the next issue. If you don't want me to share your submission, let me know.
Rock on! Eric Normand