The KISS definition in wikipedia is: “an acronym for “keep it simple stupid” or “keep it stupid simple”, is a design principle noted by the U.S. Navy in 1960. The KISS principle states that most systems work best if they are kept simple rather than made complicated; therefore, simplicity should be a key goal in design, and unnecessary complexity should be avoided”. This article summrizes its meaning in functional programming.
Simple Made Easy
Simple Made Easy emphasizes simplicity over easiness. The origin of “simple` is “sim-plex”: one fold/braid. The opposite is complex: fold/braid together. The origin of “easy” is “adjacent”: nearby. The opposite is hard: far. So simple means: one role, one task, one concept, one dimension, but not one instance or one operation. It is about lack of interleaving, not cardinality. It is objective. Easy is near, at hand, familiar: near our capabilities. Easy is relative.
Constructs are dev tools used, it helps the programmer convienience and programmer replaceability. The long term results of artifacts/use are software quality, correctness, maintenance, change. You have to understand the problem to make the solution reliable. Emphasizing ease gives early speed but complexity will slow you down over the long haul.
Many constructs are easy but complex. The generated artifacts are hard to understand and unreliable. The make things easy: install to bring to hand, learn to become familiar. Don’t complect (interleave, entwine, braid) things. Compose simple components.
|inheritance, switch, matching||polymorphism a la carte|
|imperative loops, fold||Set functions|
|ORM||delcarative data manipulation|
Partitioning and stratification don’t imply simplicity, but enable it. The complex constructs are:
|State||value and time, everything that it touchs|
|Objects||State, identity, value|
|Methods||Function and state, namesapces|
|Switch/matching||Multiple who/what pair|
|Vars||Value and time|
|Imperative loops, fold||What and how|
|Actors||What and who|
|Conditionals||Why, rest of program|
The simple contructs are:
|Construct||Get it via|
|Values||Final, persistenct collection|
|Data||Maps, arrays, sets, XML, JSON etc|
|Polymorphism a la carte||Protocols, type classes|
|Managed refs||Composing value and time|
|Delcarative data manipulation||SQL/LINQ/Datalog|
How to developme simple systems:
- Abstraction for simplicity: it not hiding, it about abstract who/what/when/where/why/how
- What: a small set of functions and a spcification of inputs, outputs and semantics. Use polymorphism contructs. Don’t complect with how.
- Who: entities implement abstractions. Build from subcomponents in direct-injection style.
- How: implementing logic, connect to abstractions and entities via polymorphism constructs.
- When/Where: use queues to avoid complecting these with anything in the design.
- Why: the policy and rules of the application. Explore rules and declarative logic systems.
- Information is simple, represent data as data, do not hide it behind a micro language and methods.
- Choose simple constructs over complexity-generating constructs.
- It is the artifact matters, not the ahuthoring.
- Simplify the problem space before you start.
- Simplicity often means more things, not fewer.
But Not Simpler
Some algebraic axes from The Axes of Abstraction:
- Associativity: Category Theory
- Commutativity: CRDT
- Distributivity: belief propagation
- Idempotency: lattices
- Invertibility: isomorphism
Generalization example from the algebra:
The Lawful Asynchronous Programming lists the benefits of math laws:
- refactor and change code safely
- allow to ignore deatils and focus on abstraction
- simplify reasoning of life cycles, finalizers and error handling
- compose components easily
This talk gives lessons learned from the failure of Scalaz Stream streaming I/O library: it starts with clever algebra, neat ideas but ends with undesirable outcomes: no associativity in append and finalizer. One should constrains the generality to make it simple.
The Scalazzi Subset
To write good functional programs, you need to follow the functional laws, especially the referential transparency. The Scalazzi Subset doc defines it as: “an expression
expr if referentially transpraent if in all program
p, all occurences of
p can be replaced by the result assigned to expr without causing an observable effect on
The safe Scala Subset is defined as to not use the following
- Side effects
- Type casing (
- type casting (
The Haoyi’s blog Strategic Scala Style: Practical Type Safety has a good explanation about the type safety.