Just right.
There is an “actual essence” of programming lurking out there and the corresponding just right programming paradigm and methodology.
Lots of very bright guys saw this one and the same “mountain” from different angles and wrote to us.
- Joe Armstrong emphasized the curse of object’s state and the universality of an “on-wire” representations of “simple structures”.
- Phil Wadler reduced composition of interfaces (behaviors) to a Set-Union relation.
- Rob Pike pushed the limits of imperative procedural languages, with emphasis on interfaces.
- Paul Graham wrote a lot and about high-level programming in Lisps.
- Rich Hickey used all the right principles for his Clojure and proved that it works with Datomic.
At a higher, system level, where common patterns emerge
- SICP popularized embedded DLSs as the just right (even universal) architecture.
- LaTeX and Emacs are actual working extensible hierarchies of such DSLs.
etc.
The main point is that the right structure is the key. It seems that the essence consist of
- Everything is a value of some type (a Set of such values) This, of course. how ve define Natural and other kinds of Numbers.
- proper, trully Abstract Data Types, where one does not know how structured the value is.
- a module is like a cell, wwhile its fucntions are enzymes.
- exproted functions (a public interface) act as “portals” or “pumps” on a cell membrane.
- only the “private” functions within a corresponding module know the implementation details.
- algebraic types show a minimal algebraic structure for all types.
- duck-typing and corresponding type-classes or Traits as the right way to define behaviors.
- parameterization of data-types, whole type-classes and individual functions
- universal pattern-matching (on data-constructors) naturally emerged out of it.
- hierarchical, layered structure, common for all spoken human languages.
- this is, of course, the Nesting, which is truly universal.
Notice that proper ADTs (as in B. Liskov), Algebraic Data Types, Inductive Types and Type-classes (as in P. Wadler) are enough for everything (Ocaml’s modules are extension of proper ADTs).
- every type must be a proper (non-leaking) ADT in the first place.
- it has to reside in its own module with “export controls”
- Ocaml modules got it exactly right. Haskell and Scala should implement them.
At the “algebra level”
- every structured value can be defined as a Product Type
- every variant is a Sum Type
- every function is an Exponent Type
- nesting at all levels is required, so
- Product-of-Sums and Sum-of-Products capture everything
Coq and Agda are minimal languages, with all core types defined in standard libraries.
In short, OO was a mistake, especially non-uniform OO as in Java and C++. The main flaw is the state.
Again, the essence is proper ADTs (which hide the inner structure) and interfaces as portals on a cell membrane, plus a hierarchy of DSLs (of human languages). Everything follows from this, including required statelessness and immutability (of maths). Algebraic Types and the Three Charting Patterns only “confirm” that this is the true essence.
The Lambda Calculus extended with advanced types and proper modules is still the basis of all languages.
CLU did almost everything right for the first time and was largely ignored.
Standard ML was the first real, industrial mostly-functional language.
Miranda was the first real pure-functional language, which has been actually used beyond research.
Erlang, being a well-researched and principle-guided language did almost everything right (being an untyped language).
MIT Scheme was another attempt to do everything Just Right.
Haskell is way ahead of everything (Erlang, Ocaml, etc) in doing things Just Right. Scala 3 seems to follow.
The core language of the Coq proof assistant is a another example of principle-guided and a minimal language. It supports Algebraic types (but not GADTs) and Inductive (recursive) types and Type-classes, so literally everything (from Booleans and Naturals) can be defined “as a library”.
Agda has similar features - almost everything in its stdlib is defined in terms of a minimal language.
This proves (by example) that the required foundation (for all programming) is indeed tiny, complementary and the necessary and sufficient features are almost orthogonal to each other – heavily syntactically sugared and much better typed Lambda Calculus.
Types are not merely Sets of Values defined in terms of a Set of Possible Operations (this is the key point), they have been generalized to become Sets of required Interfaces (which add constraints and select subsets of values), with implicit union and intersection operations (of other Sets of required Interfaces).
Modern type-system can thus be thought off as just Sets of Constraints (required interfaces). The Duck-typing is a universal notion.
In the imperative realm, Go added just right (based on decades of research) concurrency primitives to a procedural non-OO language.
There is a commonality among all of these – focus on the right principles, and strive to have a small (minimal, if not optimal) language, plus an art-like attention to details.
None of these are OO, and the OO-functionality can be (and has been) implemented as sets of embedded DSLs (in Common Lisp and MIT Scheme).
Knowing these one knows what abominations the amateur and mainstream languages really are.
The next decent language is, unironically, Rust (procedural and, unfortunately, imperative. Not even mostly-functional by default, like Scala 3).