Ep 118: Pure Parts
► Play EpisodeEach week, we discuss a different topic about Clojure and functional programming.
If you have a question or topic you'd like us to discuss, tweet @clojuredesign, send an email to feedback@clojuredesign.club, or join the #clojuredesign-podcast
channel on the Clojurians Slack.
This week, the topic is: "parts of a pure data model". We look at pure data models we've created and see what they have in common.
Our discussion includes:
- How we make pure data models
- How to organize pure data models
- What are the common parts of a pure data model?
- What are schemas good for?
- How is a functional pure data model different than an object-oriented class model?
- How does a pure data model help with maintenance?
- Semantic information verses concrete operational information.
- What about I/O?
- Input and output transforms for maximizing purity
- Why save information that the program can't use?
Selected quotes
And when there's a pure data model, a pure data model is something that is both wide enough to handle an actual use case and be useful, but it's shallow enough that you can understand it and trust the function calls that it has. All of the operations on that data are in the same namespace, so it's easier to understand.
I know they're predicates because they end in a question mark.
All of the necessary changes and views are encapsulated in a namespace. That means the rest of your application can rely on its higher-level operations when working with the data model. These are a higher-level vocabulary for your application, instead of just Clojure core's vocabulary.
Everything that can be done is all co-located in a namespace.
Am I multiplying by 0.10 or 0.15? Or am I calculating a tip? One of those statements has more information.
A pure data model lets you, as a programmer, think at a higher level in the rest of your application. When you think at a higher level that's trusted, it's a lower cognitive load. You can come back to the code later, read a function, and know what it means in the context of your application.
In every pure data model, you have to know what the data looks like.
Don't underestimate the value of being able to find places where a predicate is used. It tells you what the code cares about this situation. When you have to nuance the situation, you can look at the call sites and take them all into account.
Once you've made the HTTP call, all the information about the request, the response, the body, and all that is pure data. You can do a pure transform from the domain of raw, external HTTP information into the internal domain of the pure data model.
But because it's a pure function, it's a lot easier to test. All things are easier to test when they're pure. I/O is a very, very thin layer—both on the way in and the way out.
Instead of mixing I/O and logic, do as much I/O as you can, at once, to get a big bag of pure information to work with. And then on the way out, do a pure transform to generate everything you need for the I/O, like the full requests.
You can have a big bag of extra context that's there for you as the programmer—even though the program doesn't need it.
Parts of a pure model
- Data tree
- Schema
- Literals (eg. initial state)
- Predicates
- Data operations
- Reducing function (state + event)
- Transforms in
- Transforms out
- Views (special kind of transform)
Links
- "Reentrant Coding" Series