PurelyFunctional.tv Newsletter 425: Specify a data abstraction
Sign up for weekly Clojure tips, software design, and a Clojure coding challenge.
Clojure Tip 💡
Specify a data abstraction
I really like Ring. It makes web programming easier in Clojure. It's not perfect. But it has proven its value.
I like that it defines the spaces between the work. A maximalist framework would attempt to capture every part of web development in a mess of options and machinery. But Ring defines how that mess of options and machinery should interact and leaves the mess for someone else to build. Ring defines the interaction, not the action.
So I asked myself: What if we analyzed Ring as a method for building powerful abstractions.
Here's an informal sketch of the how of Ring:
- Specify a data abstraction that better suits your purposes than what you're building on (as in the Ring SPEC is better than servlets)
- Allow extension via functions (as in handlers and middleware)
I like this approach better than the protocol polymorphism approach, which works like this: If you want to allow different data stores, create a protocol. Create one method per operation you'd like to support. Then implement all of the operations for each datastore.
The Ring approach is different. First, you represent operation as data in a standard and general way. Then define a handler as a function that takes an operation and performs it. The implementation depends on the datastore. Define middleware as functions that transform a handler in some way. Now make an adapter that translates something into operations. When you want to swap out datastores, just swap out handlers.
The Ring method gives us a way to build solutions out of composable parts. It does so by defining a fixpoint on the data representation and how functions should compose. Protocols define a fixpoint, too, but they do so on the operations you support. Can you imagine Ring defined as protocols? The Ring method enables more higher-order composition to define solutions. We will see next week how we can apply this method to create a universal CRUD system.
This week on the podcast, I talk about the difficulty of converting undifferentiated experience into a coherent domain model.
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.
Stay healthy. Wash your hands. Wear a mask. Take care of loved ones.
Clojure Challenge 🤔
Last issue's challenge
- Sock pairs - Submissions
This week's challenge
Closest palindrome integer
An integer is a palindrome if it is written with the same decimal digits forward and backward. Your job is to write a function that takes an integer and returns the closes palindrome integer to it. The closest one could be larger or smaller than the given integer.
(closest-palindrome 100) ;=> 99 (return the smaller in case of tie) (closest-palindrome 887) ;=> 888 (closest-palindrome 888) ;=> 888
Note: If two palindromes are equidistant from the given integer, return the smallest. If the given integer is itself a palindrome, return it.
Thanks to this site for the problem idea, where it is rated Hard in Ruby. The problem has been modified.
Please submit your solutions as comments on this gist.