PurelyFunctional.tv Newsletter 384: name your callbacks
Sign up for weekly Clojure tips, software design, and a Clojure coding challenge.
Clojure Tip 💡
name your callbacks
Let's say you're using some anonymous functions in your
filters. Here's some code.
(->> customers (filter #(>= (count (:purchases %)) 3)) ;; only customers with 3+ purchases (map #(apply max-key :total (:purchases %)))) ;; get biggest purchase
This is not a ton of code, but it will make a good example. What happens if we want to name the parts so that the names reflect the intention?
There are two ways to go about it. The first way is to name the steps. The second way is to name the callbacks. I like the second way better.
1. Naming the steps
We could take the
map steps and name them based on their
(defn select-best-customers [customers] (filter #(>= (count (:purchases %)) 3) customers)) (defn get-biggest-purchases [customers] (map #(apply max-key :total (:purchases %)) customers))
Then we can put the names in place of the
(->> customers select-best-customers get-biggest-purchases)
It makes a compact and clear threading.
2. Name the callbacks
Okay, I'm calling them callbacks because I've been writing a lot of
we?), I'm going to call the function argument to
Let's see what it looks like if we name the callbacks.
(defn good-customer? [customer] (>= (count (:purchases customer)) 3)) (defn biggest-purchase [customer] (max-key :total (:purchases customer)))
Then it looks like:
(->> customers (filter good-customer?) (map biggest-purchase))
Why I like method 2 better
I mentioned before that I liked naming the callbacks better. Here's why.
First, the threading is not that much harder to read. Most Clojure
filter (and if they don't, it's time to
learn them). So this should be an easy thing to read. But, further, the
two functions are much more useful. They're applicable to a single
customer, not collections of customers. There are many more things I can
do with a function that works on one customer. You can always pass them
filter later if you need to.
That's not to mention that the functions that were extracted themselves are shorter and sweeter. They don't have anonymous functions like the ones from method 1 do.
Well, that's my opinion, anyway. I think the code speaks for itself. What do you think?
Another episode! This one is my response to Out of the Tar Pit.
Listen and watch here: My response to Out of the Tar Pit.
Clojure Challenge 🤔
Last week's challenge
The challenge in Issue 383 was to write a function that calculates the iterated square root of a number.
I love the variety of submissions. I think this is a good difficulty level to target.
Please do participate in the discussion on the gist where the submissions are hosted. It's active and it's a great way to get comments on your code.
This week's challenge
There are different ways to express the equation of a line. Here's one way:
2x + 4y = 1 x + 2y = 1
The x and the y are always the same, but we can sub out variables for the three numbers:
ax + by = c
Now, we can just make a tuple with a, b, and c:
[a b c]
The first two lines represented in this form:
[2 4 1] [1 2 1]
Okay! Now that we have a way to represent lines, your task is to write a function that takes two lines and tells us if they're parallel. Here's an example:
(parallel? [2 4 1] [1 2 1]) ;=> true (parallel? [2 4 1] [4 2 1]) ;=> false (parallel? [0 1 5] [0 1 5]) ;=> false ;; lines are not parallel to themselves
I'm looking forward to seeing the variety of answers.
Thanks to this site for the challenge idea where it is considered Medium level in Python.
You can also find these same instructions here. I might update them to correct errors and clarify the descriptions. 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.