This is a note of the Rich Hickey’s two talks:
The two talks are about the
simple concept that behind Clojure langauge and the data orientation of Clojure.
Simple vs Easy
Simple, be definition, is one fold/braid, no twist. The opposite is complex that means braided/folded together. Simple means one role, one task, one concept, one dimension. It is not one instance or one operation – cardinality is not a concern. Simple means no interleaving and it is an objective concept.
Easy mean nearby, the opposite is hard. It is at our hand, in our toolset, or familiar to our understanding/skill set. The third aspect of being easy is that being near to our capabilities. Easy is relative.
The two concepts explian the construct vs artifact: People focus on experience of use of construct: programmer convenience and programmer replaceability – these are two aspects of easy, rather than the result artifact: software quality and maintainability – where being simple is an answer.
Limits of people: reliable comes from understanding, consider a few things at a time. Interwined things are complex and undermine understanding.
To change something, you have to udnerstand it – informal reasoning about the system. Your ability to reason about your program is critical to debugging: all bugs pass type checker and tests.
Emplyasizing ease gives earl speed. Ignoring complexity will slow your down over the long haul.
What kind of runner can run as fast as they possibly can from the very start of a race? a printer. We just fire the starting pistol every hundred yards and call it a new sprint.
Somethings are easy for bing faimiliar, available and easy to use, but actually they are complex. For example, OO and ORM are easy but complex. Mental capability is limited and requires simplicity. Another example si parenthesis because it is overloaded in other langauges.
|Inheritance, switch, matching||Polymorphism a la carte|
|Imperative loops, fold||Set functions|
|ORM||Declarative data manipulatoin|
Complect means to interleave, entwine, braid.
Compose means to place together, without brading.
don’t associate simplicity with partitioning and stratification. They don’t imply it. They are enabled by it. If you make simple components, you can horizontally separate them, and you can vertically stratify them.
- State is easy because it is familiar and at-hand. But it is complex because it complects value and time. Modularity doesn’t solve its complexity.
- Object complects state, identity and value.
- Method complects function and state.
- Syntax complects meaning and order.
- Inheritance complects types.
- Switching and matching complect multiple pairs of branching decision and operations in one place in a closed way.
- Vars and varaibles complect value and time.
- Loops complects what you are doing with how to do it. Fold complects order with operations.
- Actors complect what’s going to be done and who is going to do it.
- ORM is about dual of value.
- Conditionals complect the sturcture of the program and the organization of the program.
On the other side, data is simple. It can be a map, a set. It is good for function and for communication. Clojure protocols link data and functions.
Use refereces to manage state. Use set functions to process data. Use declarative rules.
There is an inherent complexity called enviornmental complexity regarding resources.
Use abstraction for simplcity. Abstract means draw away physical nature of something: who, what, when, where, why and how. Abstraction is not
hiding – it treats the physcial nature separatly and compose them.
- What are specification of operations.
- Form abstractions from a related set of functions.
- Should be a small set.
- Represent with polymorphism constructs.
- Specify inputs, outputs, semantics and only use values and other abstractions
- Don’t complect with how.
- Who are data or entities implementing abstraction.
- Build from subcomponents in a direct-injection style.
- Pursue many subcomponents for each concern.
- Con’t complect with component details and other entities.
- How is implementing logic
- Connect to abstractions and entities via polymorphism constructs.
- Prefer declarative tools that don’t dictate how.
- Don’t complect with anything.
- When and Where
- Strenuously avoid comlecting these with anything in the design.
- Avoid directly connected objects.
- Use queues to decouple objects.
- Why is the policy and rules of the application.
- It is the hard part and spreads all over applicatoins.
- Declarative system or a rule system help.
Information is simple, don’t ruin it with objects that are complex. Represent data as data and use maps and sets directly.
Simplicity is a choice. Easy is not simple. You have to start developing sensibilities around entanglement. Reliable tools (testing, refactoring, type systems) … are safety nets, but they’re nothing more than that Choose simple constructs over complexity-generating constructs. It is the artifacts, not the authoring. Create abstractions with simplicity as a basis. Simplify the problem space before you start. Simplicity often means making more things, not fewer. Simplicity is the ultimate sophistication.
Clojure, Made Simple
Clojure delieveries faster time to market with better quality via data orientation and simplicity.
So what differentiates languages is what they make practical and what they make idiomatic.
Clojure embraces data – raw, immutable information.
- Clojure has strong data literals
- Atomic data types: BigInterger, BigDecimal, Ratio, String, Characters, Symblos, Keywords, Booleans, Regex patterns.
- Persistent data structures: List, Vector, Map, Set and everything nests.
- Code is rerpreseted as data
- Majority of functions take/return data
- Information is represented as plain data
- We can build a substantial portion of our programs using the above data types.
- The programs will be substantially smaller, simpler and more robust than OO programs that do the same jobs.
- No syntax other than the data structure: the extensible data notation – edn.
- Data structre can be used as the code, the data of different type such as HTML, and configuration.
- Syntax is the interpretation of data structure.
- Things are just lists with op at front: declarations, control structures, function calls, operators.
- Everything is an expression.
More via data
- Type annotation
- Data schema
- Logic DSL: core.log, Datalog, Cascalog
- Embedded DSL
Most things that happen between systems use one of two techniques: RPC with plain data or queues.
Systems are made with dynamic types, extensible types.
Fundamental Ideas of Clojure
we should build the insides of our systems like we build the outsides of our systems. we should communicate using immutable data inside our systems, for the same reason we do outside.
In Clojure, RPC becomes PC. Queue becomes channel.
Clojure has explict constructs for states.
- A date is a value. You can have a reference to a date. You can make that reference point to another date. But you can’t change a date.
Atomin clojure is a CAS that implements a successorship model. You say: only make this new thing if my presumption is still valid.
- The STM in Clojure can update references in Clojure things called “ref”s, for which the built-in macros “sync” and “dosync” can update one or more refs in a transactional way.
- we have something called core.async which is a channel model - a flow.
The biggest thing is moving from more specificity, which bloats your program, to more generality, which shrinks it. That is the big payoff. That is the area where you are going to get a payoff much higher than 2x. I think we suffer from in object orientation is death by specificity.
HttpServletRequest methods, a tiny part, how many maps do you see here?
getAsyncContext, getAttribute, getAttributeNames, getCharacterEncoding, getContentLength, getContentType, getDispatcherType, getInputStream, getLocalAddr, getLocale, getLocales, getLocalName, getLocalPort, getParameter, getParameterMap, getParameterNames, getParameterValues, getProtocol, getReader, getRealPath, getRemoteAddr, getRemoteHost, getRemotePort, getRequestDispatcher, getScheme, getServerName, getServerPort, getServletContext, isAsyncStarted, isAsyncSupported, isSecure, removeAttribute, setAttribute, setCharacterEncoding, startAsync, startAsync… getHeader, getHeaderNames, getHeaders, getIntHeader…
Dot completion works and no typos, but everything else is much worse
- inconsistent, idiosyncratic, wide interfaces
- much more code
- consumer code completely coupled
- hard to test
In Clojure, we just use maps. Clojure programs are smaller
- Concise code
- Dyanmic typing
- Direct support for idioms
- Generic code: informat is plain data
- Quality that a type checker can’t catch
- state management
- lack of flexibility due to specificity
- misconception hidden in morass
Most of Clojure is libraries that can be used independently. Small core is protected from bloat. A la carte polymorphism made simple by protocols that like interface without inheritance and can be extended to existing types. Clojure is extremely good at inter-op with JVM and its libs. Clojure is mature and has a ton of tools and libs.