Knowns and Unknowns

In this essay, I discuss recursive design — an extreme form of divide and conquer in terms of knowns and unknowns.

In my opinion, recursive design — divide and conquer — continues to iterate and never stops.

At some point, the design becomes so stable that it can be shipped.

[This is much like songwriting.  One never stops tweaking a song, although committing it to a recording tends to slow down the tweaking.  Performing songwriters often perform a song differently than it was recorded.]

Dissecting Knowns and Unknowns

 unkown unknown brainstorm unknown known known unknown attack early known known implement

Unknown Unknown

You cannot know what you don't know (unknown unknown).

Iterate and implement.

As design proceeds, you might encounter new sub-problems.  Iterate.

Keep iterating until all unknowns are broken down into knowns, or, the solution is complete.

Unknown Known

[Break the problem down into the other 3 categories]

Known Unknown

High risk.

Fail early.

• Mitigate the risk by trying to understand the risk.
• If the risk becomes a problem that cannot be solved, you want know as early as possible (e.g. abort as soon as possible).
• Aborting might become a re-casting of the problem, in which case re-start iterations.
• If the risk can be handled, iterate and break it down further.
• Use strong, static typing to flesh out a design (without writing implementation code) to convert unknowns into knowns.

Known Known

If you know how to solve a sub-problem:

• Defer, don't implement if any unknowns still exist
• attack unknowns first
• see brainstorming, later
• sometimes it is beneficial to implement knowns and look for new unknowns
• Implement
• Look for new unknowns that surface (mixed in with the knowns)
• Scope creep — unknowns that surface during implementation that are not formally handled constitute scope creep
• handling a new unknown should result in new iterations of the recursive design process
• Implementation can be a form of brainstorming
• Strong, static typing is a form of implementation (see later)

Brain Storming

Brainstorming is a technique of looking for new unknowns — and then iterating them.

Brainstorming is most often associated with the arts, e.g. songwriting.

Much of current programming is art — creativity that has not (can not) been formalized.

The more you know, the more creative you can be[1].

Implementing is a way to know more about a specific domain.

Static typing is a way to know more about a specific domain.

Thinking and Coding

Coding, currently, involves two activities:

• thinking
• writing code.

Strong, Static Typing

Static typing is, actually, a form of implementation — coding up the types, without coding up the implementation[2].

Haskell, Agda, etc. break implementation down into two sub-components

• signatures
• code.

Some people have a eureka moment when using statically-typed languages — they code up the type system, and, when it passes automatic checking, they write code underneath the type system and "it just works".

Coding, currently, involves two activities:

• thinking
• writing code.

Writing and checking types is the "thinking" part.

Using type-checking to brainstorm the thinking part is just "divide and conquer".  Coding is deferred until the type system has been automatically checked.

The Essence of Recursive Design

The essence of recursive design is to break problems down into smaller sub-problems.

Each sub-problem is broken down — over and over — until all sub-problems can be easily understood and implemented.

The hard part of a problem is the thinking part.  The easy part of a problem is writing code after the thinking has been completed.

If a problem seems hard, the problem needs to be sub-divided.

If a problem seems hard, one might be trying to use the wrong paradigm to solve it in.

For example, we currently think that multi-tasking is hard, but most people (not programmers) already know how to deal with multitasking, e.g. music scores, cooking recipes, etc.  Are we using the wrong paradigm to think about multi-tasking?  The functional approach is useful for one-in-one-out problems[3].  Is multitasking a one-in-one-out style of problem?  I find that StateCharts[4] and asynchronous ideas, e.g. SonicPi,[5] FBP,[6] Arrowgrams,[7] etc., make it easier to think about multitasking.

FBP

https://jpaulm.github.io/fbp/

Sonic Pi

https://sonic-pi.net/

Arrowgrams / Components

https://guitarvydas.github.io/2021/03/16/component-diagrams.html

https://guitarvydas.github.io/2021/03/06/Components-(again).html

https://guitarvydas.github.io/2021/01/16/HTML-Components-Arrowgrams.html

https://guitarvydas.github.io/2020/12/10/5-Whys-of-Software-Components.html

https://guitarvydas.github.io/2020/12/09/HTML-Components-Arrowgrams.html

https://guitarvydas.github.io/2020/12/09/HTML-Components-Handling-Callback-Logic-Explicitly.html

https://guitarvydas.github.io/2020/12/09/Box-and-Arrow-DSL-For-Concurrency.html

[1] Pat Pattison

[2] Divide and Conquer, again.  Implementation can be broken down into 2 things - (1) type system, (2) code.

[3] I call this class of problem a "calculator".

[4] https://guitarvydas.github.io/2021/02/25/statecharts-(again).html, https://guitarvydas.github.io/2020/12/09/StateCharts.html

[5] See appendix

[6] See appendix

[7] See appendix