# Ascetic Programming
## Samuel A. Falvo II
## <kc5tja@arrl.net>
### 2018 Aug 25
# Presenter Notes
I've been thinking about **Ascetic Programming** again,
after reviewing my Equilibrium sources again.
I still maintain that it's got potential to facilitate writing surprisingly sophisticated Forth programs without resorting to object orientation.
Not that there's anything *wrong* with object orientation per se;
I just find it makes using Forth somewhat alien in general case.
I find Ascetic Programming feels more natural to me.
----
# DEFINITION
## Ascetic:
Characterized by or suggesting the practice of severe **self-discipline** and
abstention from **all forms of indulgence**.
# Presenter Notes
* Here's the definition of *ascetic*... There are two aspects of this definition that I'd like to point out.
* First, on the topic of *self-discipline*.
* Forth programming always involves a ton of self-discipline.
* How many times have you willingly written code to store 0 to address 0?
* Forth, even *more* than C, will happily let you hang yourself this way.
* It does absolutely *nothing* to prevent you from doing stuff like this.
* Discipline becomes even more important when program defects are not so obvious as this.
* How many times have you been in the middle of an intense development session, only to have your *dictionary get corrupted?*
* This happens quite frequently even in an environment such as GForth,
* one of the most heavily "memory protected" Forth environments I'm aware of.
* It's not a matter of HOW the dictionary got corrupted;
* rather, it's a matter of WHY you let it happen in the first place.
* Forth's just following orders, after all.
* *Ensuring* that this never happens is *your* responsibility, and *only* your responsibility.
* You, therefore, must have sufficient self-discipline to ensure all pointers are valid,
* or you risk insanity when trying to debug.
* The second thing I'd like to point out about the definition is the focus on *indulgences.*
* Two forms of indulgence exists: **language** and **run-time**.
* Run-time
* What is OOP?
* It's a way of thinking about how components of a program interact with each other.
* you essentially anthropomorphize how software works.
* You have these units of software which *conceptually* are operating independently from each other, and which communicate amongst each other using the computer equivalent of virtual photons: *messages*.
* Read what Alan Kay has to say about what he was thinking when he coined "Object Oriented".
* Language
* One of the side effects of this anthropomorphization of code,
* you find terms such as "is-a", "has-a", and other concepts which make sense only in the context where one object *possesses* another object.
* That is, it has exclusive control over the object, or which acts as a gate-keeper for it.
* This happens frequently; read the book "Object Oriented Design Patterns" by the gang-of-four to see many, many examples of this.
* So, you have several kinds of indulgences here:
* on the one hand, objects can have possessions which leads to all manner of control issues especially in multithreaded applications,
* and on the other hand, OOP frequently requires extending the Forth language itself to support the basic concepts.
* Ascetic programming does away with all these indulgences.
----
# DEFINITION
## Ascetic Programming:
Writing modular, extensible, easy to maintain software which
**avoids the use of objects**,
and relying as great as possible on **early-bound Forth semantics.**
# Presenter Notes
* Here's my definition of *ascetic programming*. Note that it covers both of the indulgences discussed earlier.
* We dispense with object orientation
* Eliminates the **most** containment/ownership issues
* Who owns the memory? Garbage collection? RAII? Nice to have, but unnecessary.
* Law of Demeter, Single Responsibility Principle, SOLID, and other rules of thumb are *automatic*.
* Eliminates message passing metaphor.
* Relies on *relations*: **facts** are recorded and maintained in its place.
* Early-binding, not late-binding, allows for significantly easier time debugging when things go wrong.
* Sometimes, containment and late-binding is advantageous
* Made explicit in the code where this occurs.
* We dispense with language enhancements
* Which syntax, exactly? FIG-Forth? Forth-79? ANS Forth? Forth-2012?
* Minimizes dependencies
* Easier to publish code
* Easier to reason about the code
* Eliminates duplication of intent in code
* OOF code calling Wil Baden's OOP code, which calls SwiftForth OOP code, etc.
* We rely on **self-discipline** much more heavily than other programming approaches.
* I rely on **Declarative, Inquisitive, then Imperative** programming conventions to habituate this discipline into the subconscious.
* With only a small amount of practic,e you'll find writing this way becomes largely automatic.
* Per Chuck Moore: Things which can happen earlier in the development cycle **should** happen earlier. **Edit-time**, **Compile-time**, then **Run-time**, in order of preference.
* **Smaller, simpler, faster, easier to debug, and easier to write.** These are all significant wins which should not be underrated.
----
# Concepts
* **Entities** replaces **objects**.
* *Identity*, but no *address*.
* `entity ( -- n )` allocates a new entity ID.
* `retired ( n -- )` frees an entity ID.
# Presenter Notes
* An **entity**
* has a unique identity, but not a unique **address** in memory.
* will be used frequently as a *foreign key* in many data structures.
* `retired` may or may not actually do anything. For a 16-bit Forth, it's wise to re-use IDs. For a 64-bit Forth, you'll die of old age before you run into a case where all 7.5 billion people on Earth allocates entity IDs such that it wraps around.
---
# Concepts
* **Relations** replaces:
* typing
* containment
* *more?*
* **Relations** are strategic, not tactical.
# Presenter Notes
* Once you can identify entities, you can record facts about them in various tables.
* NOTE: table is conceptual here; maybe you're storing data in a tree instead. Whatever; details aren't important at this high-level overview.
* **Relationships** implies set-membership, which is more expressive than any type system, static *or* dynamic.
* Most uses of containment in object oriented programming is to maintain a close relationship with the contained object.
* Model/View/Controller -- when a model changes, the model has to update all views on that model. Controller needs to know what the model is. Etc.
* Strategic code is problem-oriented; tactical code is boilerplate.
* Experience: only about 20% of classes in OOP programs are problem-oriented. 80% are factories, configuration management, or other boilerplate needed to lubricate the smooth functioning of the remaining 20%.
----
# Demo and Code Review
Imagine a simple game of **Connect-Four**.
* It's hard to play a game without an interactive interface.
* A Game Board, which consists of 7 columns of 6 rows.
* Score indicators
* Up to 42 checkers
* Human player
* Computer player
* Mostly early-bound, but some late-bound behaviors!
These are independent things which *may or may not* be visible at any given time.
----
# Modules
* Responsible **only** for its own memory management.
* Public vs Private Interface
* **Generally**, private words are imperative in nature.
* **Generally**, public words are inquisitive or declarative in nature.
* **Public interface always idempotent.**
Example:
!forth
create database /database allot
variable #records
. . .
: appear ( e - ) ... ;
: (invis) ( e r# - e r# ) 2dup cells database + @ =
if 2drop rdrop -1 then ;
: visible? ( e - f ) 0 begin dup #records @ < while
(invis) 1+ repeat 2drop 0 ;
: -vis ( e - e ) dup visible? if drop rdrop then ;
: visible ( e -- ) -vis appear ;
# Presenter Notes
* Even from this nonsensical example, some things become apparent:
* `visible` is side-effecting, but is also idempotent. I can run it once, or a hundred times; the outcome will remain the same.
* Most or even all declarative words have corresponding inquisitive words as part of their implementation. **CRUD**-like interfaces are easy and largely automatic.
* *Internal* definitions have shorter, more mnemonic than self-documenting, names. These are generally OK to shadow in subsequent modules.
* Internal words frequently have sophisticated control flow (R-stack manipulation).
* Notice how public interface words *hide* these details from the caller though.
* This module defines its own data store for maintaining state. Words like `appear` and `(invis)` know how to manage this memory.
* Modules are *entirely* self-contained. They don't even know how *entities* are represented (except insofar as that they are represented in a single cell on the stack).
* DItI is not required, but it sure does help. As indicated above, if you know DItI's rules, you can reason about each definition *in complete and total isolation from even its dependencies.*
* My examples use statically allocated buffers. However, this is an implementation detail. Your modules are free to use dynamically allocated memory if you want. B-trees, skip-lists, hash tables, etc. It's all up to you.
----
# Polymorphism
* Visible objects are great, but what do they *look like?*
* Objects are a good abstraction for user interfaces, because you can ascribe shapes to *classes* of objects.
* Buttons, menus, players, bullets, spreadsheet cells, etc.
* Whether an object is visible/drawn or not is a declarative matter; ideally expressed using *ascetic* techniques.
* Object-specific behavior is best addressed using imperative *methods* or *callbacks*; best expressed using late-bound techniques.
Let's update our `visible` interface to accommodate this **fact**:
!forth
: visible ( xtDraw e -- ) -vis +row dup ent! swap xtDraw! appear ;
**IF** it's not already visible **AND** there's room for another row in the database **THEN** record the entity and draw procedure in the table,
then make it **appear**.
----
# Summary
* Principles
* Modules are solely responsible for managing their own memory. **No exceptions.**
* Modules encapsulate **knowledge** (facts as they pertain to one or more entities).
* Entities have identity, but not proximity.
* Non-polymorphic public interfaces are declarative and inquisitive in nature.
* Polymorphic interfaces (e.g., callbacks) generally tend to be imperative in nature.
----
# Summary
* Cavaets
* Ascetic programming is not fast programming. Keep a dictionary and thesaurus handy, as both Moore and Knuth recommended.
* Ascetic programs are not fast programs. You don't have the luxury of following pointers to get to relevant data.
* **BUT**, ascetic programming is *fast enough* for most needs, especially if you are more clever about your module's storage model.
----
# Summary
Just as *Test-Driven Development* enables *Extreme Programming*,
so too does *Declarative, Inquisitive, then Imperative* enable *Ascetic Programming*.
----
# FIN.
Thank you for your attention and questions.