Are We There Yet? — Rich Hickey Rich Hickey, Clojure.org
Are we being well served by Java and its ilk? Have we reached consensus that this is the best way to build software? Is there any evidence that this is so? Is OO in the tradition of Java a known good, or just so widely adopted we no longer have the ability to see its attendant costs or limitations? Are we ready for an increasingly concurrent and heterogeneous world, or will we be facing an fundamental impedance mismatch? What pressures should drive the adoption of new (and often old) ideas not yet in the mainstream? This talk will advocate a reexamination of first principles including state, identity, value, time, types, genericity, complexity and information representation, and admit the possibility that not only are we not there, we may be driving the wrong car, on the wrong road. We'll take a look at some interesting ideas to consider as we determine 'what's next'.
- Is OOP the best way to write software?
- single-dispatch, stateful, OOP languages are entrenched
- differences between these languages are superficial (ex: syntax, availability of closures, etc.)
- key ideas: complexity, time
- "Seek simplicity, and distrust it." - Whitehead
- bad tools and solutions make us less efficient in small ways
- complexity is obvious, but sometimes complexity is hidden behind a facade of simplicity
- C++ is easy to learn, but is it really simple?
- same syntax to refer to things on the heap and things not on the heap
- what do you do with objects? no standard automatic memory management - big source of incidental complexity
- the language doesn't make it clear that memory management is the programmer's responsibility
- ditto Java
- even removes the complexity of handling GC
- hidden complexity: mutable vs immutable objects; not just a concurrency problem, a complexity problem
- most programmers measure complexity in superficial ways: syntax, expressivity
- we want encapsulation so we can build things on top of other things more simply; "someone building a house doesn't have to worry about the inside of a brick"
Pure Functions Are Worry-Free
- local scope
- takes and returns values
- has no notion of time
- easy to understand and change
- problems with functions, though:
- "programs in general are not functions"
- programs run indefinitely long
- users need to see the program's behavior
- couldn't express Google search as a function, it's a process
OO and Change
- behavior and time are almost meaningless in OOP
- there's no real concept of time in objects, they don't have a reified notion of time, but objects pretend to manipulate time
- "the OO model has gotten time wrong"
- we've made objects that can change in place, and we've made objects that we can _see_ change in place
- no proper notion of values
- "No man can cross the same river twice."
A Simplified View
- "We've invented mutable objects, we need to un-invent them."
- actual entities are atomic immutable values
- we superimpose mutable models on collections of immutable values
- identities are mental constructs
- the future is a function of the past
- objects are not things that change over time
- "There is a becoming of continuity, but not a continuity of becoming."
- Value: immutable magnitude, quantity, number, or immutable composite thereof
- Identity: a relative entity we associate with a series of values
- State: value of an identity at a moment in time
- "mutable state makes no sense (given this definition)"
- Time: relative before/after ordering of causal values
Why should we care?
- our programs need to make decisions
- mutable way of thinking is a useful mental model, makes things easier to conceptualize
- but we can't screw up time and immutability because then things are wrong
- we don't make decisions about the world through direct contact between our brains and the outside world - we don't take our brains out and rub them on things we want to experience
- we also don't get to stop the world to look around
- human perception is uncoordinated and massively parallel - it's NOT message passing
- we are always perceiving the unchanging past
- we perceive based on discrete events - we like snapshots (they're like values)
- are brains are simultaneity detectors
- making things happen are perceiving things are completely different
- pure functions - we have this
- language constructs for representing values, creating them and sharing them
- coordinate the succession of values
- we have to consume memory to model time
- old value -> pure function -> new value (non-destructive, consumes memory)
- old values can be used as perceptions/memory
- GC will clean up the "past" that's no longer referenced
- persistent data structures
- ideal for states, snapshots, and memories
- stable values for decision-making and calculation
- the next version of a value shares structure with previous value, so usually efficient
- creating a value never disturbs prior values, nor impedes perceivers of prior
- "your life is going to be better if you use these because there's a huge quantity of things you'll no longer have to worry about"
- usually based on trees (with high branching factors, usually shallow)
- support structural sharign
Declarativeness and Parallelism
- must be more declarative, less looping
- tree-based persistent data structures work well for this
- compositional building blocks
- don't perform as well - better for reads than write, but...
- nobody can see what happens inside operations
- more cores, and the parallelism starts to match up to mutable data structures
- need to ensure atomic succession
- need to provide point-in-time value perception (snapshots)
- multiple timelines support multiple threads of control - no "I'm the program, I control the universe" model
- models: CAS, Agents, STM, locks as a way to enforce timelines
- perception in STM via multi-version concurrency control
- no interference with processes
- cheap historical snapshots
- allows observers/readers to maintain a timeline
- composite snapshots form glimpses and visual scans, thinking in terms of human vision
- "there is no one STM; if you want to beat up on STM, pick an implementation"
- granularity matters; if you find yourself requiring a transaction to perceive a consistent value, you're doing it wrong
- excessive implicit complexity begs for change
- conflation of behavior, time, identity, and state is a big source of implicit complexity in OOP
- "pay attention to the FP people, look at the great properties of pure functions"
- epochal time model is worth trying - be explicit about time
- we can do multiple implementations, we have the infrastructure to support it
- hard problems: tying STM to things like IO, coordinating with the rest of the world
- "become as happy as you can with the lack of control - that will give you more concurrency"
- more data structures and time constructs to come
- open question: is there a way to reconcile all this with OOP?