PurelyFunctional.tv Newsletter 343: Tip: read the exception message
Sign up for weekly Clojure tips, software design, and a Clojure coding challenge.
Clojure Tip 💡
read the exception message
Amid all of the complaints about the length of Clojure stacktraces, there is a dirty secret: many people don't read the error message and try to understand it.
Yes, I know that you do. But people ask me a lot of questions, and too often the answer is right there in the exception message, including the line number. Just to be clear, I'm happy to receive the question and take the time to answer it. However, I hope I can make people a little more self-sufficient by developing a habit of reading the exception message.
Clojure's default configuration for a while has been to not print the stacktrace at the repl when there's an error. It only prints the exception message, file name, and line number. It used to print the whole stacktrace, but that was a while ago. And recent versions have only gotten better about being useful. Here's an error message from Clojure 1.10.1:
If you do get a stacktrace in your console, due to configuration or tooling, all is not lost. Scroll back in the output, looking for the exception message. The message is in there. It's not very ergonomic to have to scroll and visually scan through 60-100 lines of stacktrace, but it's worth it. The exception message is the most information-packed part of the output, and the top of the stack (the next lines of output in the stacktrace) are more likely to show the source of the error than the last lines. There are important exceptions, but starting at the top is the rule of thumb.
Stacktrace frames are printed indented. So look for unindented lines that are flush-left. These are probably your exception messages.
The filename and line number are very informative. They pinpoint the problem. But the message is not, and neither are the stack frames. When this is the case, it makes sense to look for a causing exception. JVM exceptions can have a chain of causes. Code can catch an exception, wrap it in a new one, then throw the new one. The old one is still there, and it will be printed out, too.
I scrolled down and found this:
That one does give a better message. I believe the repl's error printer uses both of these exceptions to form a nice message. It's just not always available.
Book sale 📖
I was just informed that Manning is having a sale today (Monday). All MEAP ebooks are \$20 and all MEAP print books are \$25. That's essentially 50% off of my book. It's a great time to pick it up, and any other MEAPs that catch your fancy.
You can find my book, Grokking Simplicity, here.
Currently recording 🎥
I did not publish any new lessons in the Property-based Testing course. I blame the difficulty of testing distributed systems.
I've been working on a lesson that teaches how to test distributed systems. It's tough, but PBT at least has an answer. Trying to make that answer accessible is what takes a long time.
So allow me a question: does this topic interest you? I haven't had nearly as many sales as I thought for this course, so I'm wondering if I should cut bait now and move onto a topic that you are more interested in. Please reply if you're into it, or even if you're not. My main goal is to help you and the rest of the community. If I'm not doing that, I need to move on.
Brain skill 😎
find something challenging
When you're learning something new, one of the main obstacles is a lack of motivation. You might intellectually know you should learn a topic, but that doesn't mean your brain will start to absorb it.
One trick to encourage your brain to learn is to challenge it. Challenge is a positive emotion, and positive emotions are sought after by your neurons. Challenge doesn't just mean "hard". It means you feel compelled to do it because it is hard. You're looking for something difficult that gets your juices flowing.
The challenge you choose could be anything, so pick something closely related to your goals. It could be a big project, or an artificial deadline, or some arbitrarily motivating target. For instance, you could try to program an entire web app without any mutation. What's important is that you rally your resources when you think about it.
Clojure Challenge 🤔
Last week's challenge
The challenge in Issue 342 was to model ajax state.
You can check out the submissions here.
Well, as I write this there are no submissions. I did put one together.
This week's challenge
make an action idempotent
Clojure.core has a function called
memoize. You probably already know
what it does, but just to be on the same page,
memoize takes a
function and returns a new function. The new function is just like the
argument you pass it, but it has a superpower. It's now cached. It will
remember any arguments you pass it, and the return value it found, and
give it to you without doing all the work. Of course, it has to do the
work the first time, but after that, it's all cached.
You can do this same thing, but instead of giving a function a superpower of being cached, you can make it idempotent. Let's say I had a function that connected to an SMTP server to send an email. I don't want to send the email twice, but I want to be able to retry in case it didn't succeed. So I make the action idempotent so the duplicates don't matter. And what does it mean to be the "same email"? There needs to be a notion of identity.
Write a function called
idempotent-by that takes a function and
returns a new function that is like the original except calling it twice
on the same thing does the action only once. The first argument to
idempotent-by, before the function in question, should be another
function that defines the key. Just like
group-by takes a keyfn,
idempotent-by takes a keyfn.
(defn idempotent-by [keyfn f] )
As usual, please send me your implementations. I'll share them all in next week's issue. If you send me one, but you don't want me to share it publicly, please let me know.