Features of a dream programming language
Tags: programming, languages, computerscience, design
Published: 2021-01-08 01:31:41

**Update:**

Be sure to read the [improved 2nd draft of this article](https://dev.to/redbar0n/features-of-a-dream-programming-language-2nd-draft-p7j) instead. The following content is the original article, only saved for posterity, and to respect the original URL. This article garnered some [fruitful discussion on Hacker News](https://news.ycombinator.com/item?id=29716998), and some comments here on dev.to you can read at the end.

## [](#first-a-few-overarching-guiding-principles)First, a few overarching guiding principles:

*   **All programming languages are, in actuality, built to overcome human limitations.** Otherwise, one might as well be typing 0's and 1's, or a lower-level language like Assembly.

    *   Software architecture in general (and frameworks in specific) is _a way to organize the mind_ of the developer(s), categorising the conceptual world into what's closely or merely remotely related (giving rise to principles like 'cohesion over coupling' etc.). (This might explain OOP's popularity.) The machine would be perfectly content with executing even spaghetti code. Inspired by [Martin Fowler](https://www.goodreads.com/quotes/6341736-any-fool-can-write-code-that-a-computer-can-understand).
    *   A programming language has certain [affordances](https://www.interaction-design.org/literature/topics/affordances), allowing you to talk specifically about/with some concepts (typically the [first-class citizens](https://en.wikipedia.org/wiki/First-class_citizen) of the language), and avoid having to talk about other things (e.g. memory management, language runtime concerns). This does not only apply to [DSL](https://en.wikipedia.org/wiki/Domain-specific_language)'s.
    *   "each programming language has a tendency to create a certain mind set in its programmers. ... you tend to have a mental model of how to do things based on that language. ... Such a mind set may make it difficult to conceive of solutions outside of the model defined by the language." - [Dennis J. Frailey](https://www.quora.com/What-did-Alan-Kay-mean-by-Lisp-is-the-greatest-single-programming-language-ever-designed/answer/Alan-Kay-11?comment_id=46099908&comment_type=2)
*   Only by accounting for human limitations (like cognitive capacity, and familiarity), could one derive a specification for the ideal programming language.

*   **A bug is an error in thinking**. Either by the developer, or the language-designer for not sufficiently accounting for human psychology ([Sapir-Whorf: the language you write/speak determine what you can/do think](https://talks.golang.org/2015/simplicity-is-complicated.slide#6)).

    *   To reduce bugs, a language should ensure **simple, safe, and scalable _ways of thinking_**. For instance:
        *   [Type systems](https://en.wikipedia.org/wiki/Type_system) are a way to use the compiler to help us [verify our beliefs](https://twitter.com/garybernhardt/status/1391800319609171968?s=20) about our own code: they help us think consistently.
        *   [Closures](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36), enables the programmer to specify and share behaviors that are already half-way thought through and defined.
        *   [Transducers](https://medium.com/javascript-scene/transducers-efficient-data-processing-pipelines-in-javascript-7985330fe73d), allows the programmer to define and compose behaviors without having to think explicitly about what’s behaving.
        *   [Currying](https://en.m.wikipedia.org/wiki/Currying#Motivation), allows the programmer to take one grand behavior and break it down into smaller behaviors that can be reused independently, or used in sequence.
        *   Composition allows the programmer to think & build piece by piece, instead of all at once, and without the context influencing too much.
        *   "[Open for extension](https://youtu.be/ltCgzYcpFUI?t=1421), closed for modification": A programmer can recognize something useful, and add more pieces to it, without having to change the original thing (e.g. extension methods in C#), and without tying those new pieces too closely with the original thing (e.g. subclassing), thereby limiting their reuse.
*   A language determines **WHAT** you can & have to think about, but also **HOW** you have to think about it.

*   **"Things that are different should look different"**. Inspired by [Lary Wall on Perl's postmodernism](http://www.wall.org/~larry/pm.html) and [my own frustrations with modern component frameworks like React](https://dev.to/redbar0n/what-happened-to-components-being-just-a-visual-thing-22hc), and my impression that Lisp/Clojure is hard to learn precisely because it has so little syntax: when everything looks the same it is hard to [tell things apart](https://www.codeproject.com/Articles/1186940/Lisps-Mysterious-Tuple-Problem). Although it is prize-worthy to **stay very frugal with syntax**, since more syntax necessitates more learning/documentation (knowledge debt, info overload), more avenues for confusion (the best code is no code), and more complications (language intricacies can lead to software intricacies, which can lead to bugs). My philosophy leads more towards [Golang](https://hackernoon.com/the-beauty-of-go-98057e3f0a7d) ([less features](https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html), readability is realiability, simplicity scales best) and [Python](https://www.python.org/dev/peps/pep-0020/) (explicit over implicit, one way over multiple ways), than [Ruby](https://m.signalvnoise.com/provide-sharp-knives/) (provide sharp knives) and [Perl](http://www.wall.org/~larry/pm.html) (postmodern plurality, coolness/[easiness](https://www.infoq.com/presentations/Simple-Made-Easy/) is justification enough in itself). Even though I come from Ruby and love it, and also cannot help admiring Lisp for its elegance and [crucial evolvability](https://www.youtube.com/watch?v=_ahvzDzKdB0).

## [](#purpose-what-should-this-dream-language-of-mine-primarily-be-for)Purpose: What should this dream language of mine primarily be for?

*   Webapp / app + systems development. In Rich Hickey's words: [Information-driven situated programs](https://youtu.be/2V1FtfBDsLU?t=1607). Ideally, also open to extension into [more areas of programming](https://github.com/nagydani/seedling/blob/master/RATIONALE.md).
    *   Scripting and prototyping, but also scalable to production use (app/webapp)
    *   Systems development (compiled)

So, from various sources of inspiration, and these principles in mind, here is the list of features that my dream programming language would have.

## [](#features)Features:

Features in **bold** are considered most important.

*   **Readability and reasonability** as top priority. Reduce dev mind cycles > reduce CPU cycles. Human-oriented and DX-oriented. Willing to sacrifice some performance, but not much, and not to overly gain comparability with natural language (counter-inspired by SQL; inspired by Cypher). Willing to sacrifice immediate power in the language itself, esp. if that can be achieved through abstracted-away libraries.

    *   Should always be able to be read top-to-bottom, left-to-right. No `<expression> if <condititonal>` like in Ruby.
    *   Readability should not imply a one to one match with natural language (counter-inspired by SQL), since natural language is inconsistent, duplicitous, ambivalent and multi-faceted. Consistency is key to a programming language. But it should borrow some similarities from natural language (like its popular Subject-Verb-Object structure) to make adoption easier (more at-hand/intuitive).
    *   **Encapsulation**. Everything should be able to be encapsulated (all code, whether on back-end and front-end), since **encapsulation affords reasonability** (and testability), by limiting places bugs can hide. Counter-inspired by Rails views (sharing a global scope) and instance variables. Inspired by testability of React components.
    *   No need to keep things in human memory for more than about 20 lines of code at a time. If extending that, then there should be an conceptual model which can be carried forward incrementally in the programmer's mind (implying state and mutation at the conceptual level, but not necessarily implemented as such).
    *   No need to manipulate data structures in the human mind. Programmer should always be able to **_see_ the data structure** he/she is working on, at any given time, in the code. Inspired by [Bret Victor](http://worrydream.com/), and Smalltalk. So the language should make that easy for tooling to support. But without being a whole isolated universe in its own right like a VM. Counter-inspired by Smalltalk. Some have described this as [REPL-driven-development](https://purelyfunctional.tv/lesson/what-is-repl-driven-development/), or interactive-programming. Inspired by Clojure. But without having to leave your IDE to code in a console/prompt/terminal. Inspired by [QuokkaJS](https://quokkajs.com/).
    *   Params: Function parameters must be named, but no need to repeat yourself, if the argument is named the same as the parameter (i.e. [keyword arguments can be omitted](https://bugs.ruby-lang.org/issues/14579)). Inspired by JS object params, and Ruby. Counter-inspired by the [Mysterious Tuple Problem](https://www.codeproject.com/Articles/1186940/Lisps-Mysterious-Tuple-Problem) in Lisp. If currying, then input params should be explicit at every step (for clarity and refactorability). Counter-inspired by Point-free style in FP (since "explicit is better than implicit", inspired by Python).
    *   No [Place-oriented programming (PLOP)](https://youtu.be/YR5WdGrpoug?t=1213), iow. avoid order-dependence at almost any cost, since it isn't adaptable/scalable. This goes for parameter lists to functions etc. Don't want to have to use a '_' placeholder for places where there _could_ be a parameter, just because you didn't supply one. Consequence: use primitive data structures like dictionaries over records/fields. Inspired by Clojure.
    *   No `unless` or other counter-intuitive-prone operators. Counter-inspired by Ruby.
    *   No abstract mathematical jargon. Counter-inspired by Haskell. Should be accessible for as wide a community as possible, with as little foreknowledge as possible. Inspired by [Quorum](https://quorumlanguage.com/).
    *   Do not presume contextual knowledge. In UX this is known as "No modes!". Code should be able to be read A to B without having been educated/preloaded with any foreknowledge (like 'in this context, you have these things implicitly available'). Counter-inspired by class inheritance and Ruby magic, and JavaScript's [runtime bound](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) `this` keyword and associated [scoping problems](https://kentcdodds.com/blog/classes-complexity-and-functional-programming). Turns out too much dynamism (runtime contextualisation) can be harmful.
    *   Should facilitate and nudge programming in the language towards code with low [Cognitive Complexity](https://www.sonarsource.com/docs/CognitiveComplexity.pdf) score.
    *   **Dynamic Verboseness**: Should be able to show more/less of syntax terms in the code. Beginners will want more explicity, as code can be more self-explaining. Whereas professionals will want to write less and have more implicit/hidden from sight, so they can focus on their the problem domain without clutter from the language. See: content-addressable code.
*   Not indentation based (counter-inspired by Python), since it is brittle. But also not require semicolons. Inspired by Ruby, and semi-colon-free JS.

*   Fast feedback to the programmer is second top priority. Inspired by TypeScript hints, [QuokkaJS](https://quokkajs.com/) (!), Webpack Hot Reload, and Expo Live Reload.

    *   REPL / interactive shell. Can be done even if compiled, by having an interpreter on top of a VM to the compiler.
*   **Refactorability / change-ability.**

    *   Similar-looking and non-interacting code-lines should be able to change place without breaking anything. Counter-inspired by not able to add comma to last line in JSON, not being able to reorder/extract from comma-separated multi-line variable declarations in JS, and also counter-inspired by the contextualised expression terminators in Erlang.
    *   **Backward-compatible and forward-compatible**. Should be able to not worry about (or make poor future tradeoffs due to) backwards-compatibility. ([Counter-inspired by ECMAScript](https://maggiepint.com/2017/04/11/fixing-javascript-date-web-compatibility-and-reality/).) To make the language optimally and freelly evolvable, and worriless to upgrade. [Backwards-compatibility](https://en.wikipedia.org/wiki/Backward_compatibility) and [Forwards-compatibility](https://en.wikipedia.org/wiki/Forward_compatibility): Code in a one language version should be transformable (in a legible way) to another version (both ways; backwards and forwards). Solutions could be to either: have simple CLI tools to **automatically refactor old code to new language versions**, to always stay optimally **adaptable**, without having breaking changes. Maybe using some form of **built-in self-to-self transpilation**. Will likely need to be able to treat code-as-data. Might need compile-time macros. Or a solution could be to: with every breaking language revision, include an incremental language adapter, which would allow upgrading whilst ensuring backwards compatibility. Could be solved with [Mechanical Source Transformation](https://go.dev/blog/gofmt), enabled by gofmt, so developers can use [gofix](https://go.dev/blog/introducing-gofix) to automatically rewrite programs that use old APIs to use newer ones. Which is crucial in **managing breaking changes**. A breaking (aka. widely deviating) change, should in effect, not actually break anything (that current languages and systems do, is considered a "pretty costly" design flaw).
    *   [A language for library authors](https://youtu.be/ltCgzYcpFUI?t=1317). Inspired by the success of C++. The language should be able to **evolve by community convention, not by centralised specification**: the language itself should be extensible with libraries (would probably need to have compile-time macros).
    *   [Content-addressable code](https://youtu.be/gCWtkvDQ2ZI?t=170): names of functions are simply a uniquely identifiable hash of their contents. The name (and the type) is only materialized in a single place, and stored alongside the AST in the codebase. Avoids renaming leading to breaking third-parties, and avoids defensively supporting and deprecating several versions of functions. Avoids codebase-wide text-manipulation, eliminates builds and dependency conflicts, robustly supports dynamic code deployment. Code would also need to be stored immutably and append-only for this to work. All inspired by [Unison](https://www.unisonweb.org/).
    *   Convention over configuration (CoC). The language should be "open to extension" by any community, without permission. So that it can evolve and converge to a consensus, based on real-world experience and feedback. Not being completely and statically "configured" up front, which would entail predicting future uses, and consequences of those uses. (Thus, the feature-list you are now reading would thus just be a first draft, of course.) This is mirrored in the important talk [Growing a Language, by Guy Steele](https://www.youtube.com/watch?v=_ahvzDzKdB0), and the point on [crucial evolvability](https://www.youtube.com/watch?v=_ahvzDzKdB0). NB: "if you apply [CoC] dogmatically you end up with an awful lot of convention that you have to keep in your head. It's always a question of balance; Hard Coding vs. Configuring vs. Convention, and it's not easy to hit the optimum (which depends on the circumstances)." as [Peer Reynders reminded us](https://dev.to/peerreynders/comment/1j4c4).
*   Modularity. Module system which is sensible. Inspired by the NodeJS controversy. Code-splittable and [tree-shakeable](https://blog.cleancoder.com/uncle-bob/2018/04/13/FPvsOO.html). Inspired by Rollup.

*   **Quick to get started and produce something.** Inspired by JS. Counter-inspired by JS tooling.

    *   Not too unfamiliar (to a large group of programmers, and to what they teach in universities). "Familiarity and a smooth upgrade path is a really big deal." [source](https://youtu.be/QyJZzq0v7Z4?t=1997)
*   Sensible, friendly, and directly helpful error messages. Inspired by Elm.

*   **Struct-Oriented Functional Programming (SOFP)**. Mimics the style of object-orientation, but is simply structs and functions under-the-hood. (Also: functional programming patterns over procedural code.) Because it is human to see the world in terms of verbs and objects. Focusing to heavily on only one of the paradigms (OOP or FP) will either lead to anti-patterns (God classes/objects, Factory objects, and Singletons, in OOP), or program structures far removed from the business domain model which also has linguistically unintuitive syntax (since only 12% of natural languages start with the verb, either Verb-Subject-Object or Verb-Object-Subject, [ref](https://en.wikipedia.org/wiki/Subject%E2%80%93verb%E2%80%93object)), as in pure FP. So [Subject-Verb-Object](https://en.wikipedia.org/wiki/Subject%E2%80%93verb%E2%80%93object) should be preferred. (88% of natural languages start with something concrete, the Subject/Object, which I think is a reason for OOP's success; it is more intuitive for beginners, which is vitally important for onboarding & growth.) "Objects and methods" could be merely syntax sugar for structs and functions (see: [interchangeability of method-style and procedure-call-style](https://youtu.be/QyJZzq0v7Z4?t=1343), or the [pipe first operator](https://rescript-lang.org/docs/manual/v8.0.0/pipe) in ReScript, which also illustrates _emulating object-oriented programming_), if one leaves out troublesome inheritance (which might be good, since composition > inheritance). Inspired by Golang.

    *   Functional programming patterns like `.map`, `.filter`, over procedural code like `for-loops` etc., since the latter would encourage mutating state, and we want immutability.
    *   [Tree-shakeable code](https://medium.com/@Rich_Harris/tree-shaking-versus-dead-code-elimination-d3765df85c80) (for client-server webapps). So it should need a source code dependency between the calling code and the called function. Which makes the language more FP than OOP, according to one [definition of FP vs. OOP](https://blog.cleancoder.com/uncle-bob/2018/04/13/FPvsOO.html). In general, shifting concerns from runtime to compile-time is considered a good thing, as it makes the language more predictable, optimizable, and affords helpful coding tools. Having consequences of code changes appear at runtime is a bad thing (see: [The Fragile Base Class problem of OOP](https://en.wikipedia.org/wiki/Fragile_base_class))
    *   [Referentially transparent expressions](https://medium.com/@olxc/referential-transparency-93352c2dd713). Which means variables cannot be reassigned, so a name will always refer to the same value (see principle: "Things that are different should look different"). Inspired by Haskell. This feature should also lead to easy automatic parallelization and memoization.
    *   [Formally verifiable](https://youtu.be/yiiDFRs62lQ?t=1290) / provable. Nice-to-have, not must-have.
*   Parallelization made natural. [Aided by pure functions](https://en.wikipedia.org/wiki/Purely_functional_programming#Parallel_computing). The language should make it easy/natural for programmers to use parallelism (nudges), through language constructs (like [executing several sequential lines simultaneously](https://youtu.be/XlvfHOrF26M?t=109)), to avoid [overly sequential thinking](https://news.ycombinator.com/item?id=28213940), which leads to suboptimal performance due to the fact that programmers/humans think sequentially. Inspired by [Verilog's fork/join](https://en.wikipedia.org/wiki/Verilog#Fork/join) construct. But as opposed to the fork/join example, the language should enforce a deterministic order, guaranteed implicitly by the lines sequential top-down order of appearance in the code (a novel idea, which would need to be experimented with thoroughly..). Alternatively, take [inspiration from Golang's elimination of the sync/async distinction](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/) and allow programming everything in a sequential manner, but do parallelism under the hood. The sync/async barrier elimination, however, doesn't necessarily nudge programmers towards using parallelization (spinning off new threads) within the context of a program (thread). That style might conflict, or it might be synergistic with the goal of nudging programmers towards making more use of parallelization. Ideally, the language runtime should be able to use parallelization to handle multiple independent processes (like client/server requests; goroutines for concurrency), but also _automatically distribute a single program across multiple CPU cores (without special directives, like thread/go)_ when those cores are idle. To do that, the language should nudge towards natural use of multi-threading instead of single-threading. But not at the expense of readability/reasonability, which is the top priority. The programmer should be concerned with, and simply describe independent sets of causal/logical connections, and the language runtime should automatically take care of as much parallelization as possible/needed.

*   Compiled, but also interpreted and/or incrementally compiled (for dev mode). Inspired by C++ and JS.

    *   Interpreted / incrementally compiled: So developer can write quick scripts and get fast feedback. Sacrifices runtime speed for compile-speed. Except it also needs quick startup/load time.
    *   Compiled: For production. Sacrifices compile-speed for runtime speed. Compiles to a binary. Inspired by [Deno](https://youtu.be/SYkzk_j3yb0?t=1334).
    *   [Small core language](https://youtu.be/yiiDFRs62lQ?t=1167): Compiled down to a small instructions set, which can be used/targeted as a starting point to generate code for other programming languages (i.e. generate JS).
    *   [Portability](https://youtu.be/yiiDFRs62lQ?t=1245): Be able to target and run on multiple computer architectures.
*   **Mutable API, but immutable under-the-hood**. Immutable/[persistent data structures](https://en.m.wikipedia.org/wiki/Persistent_data_structure) (like [Lean-HAMT](https://bendyworks.com/blog/leveling-clojures-hash-maps)) and structural sharing, to allow incremental update, while also avoiding duplication of data. Inspired by Clojure. Alternatively: [In-place mutation](https://docs.rs/im/13.0.0/im/#in-place-mutation), where data structures only become immutable when they're shared (presumes keeping track of borrowing / reference counting). Inspired by Rust. The immutability will also facilitate concurrency and avoid race-conditions. As a bonus you could get time-series and thus time-travel for data. The desirability of a mutable API (mutating objects instead of always having to pass in functions) is inspired by the JS libraries [Immer](https://immerjs.github.io/immer/) and [Valtio](https://codesandbox.io/s/zustand-valtio-jotai-counters-my6np?file=/src/ValtioCounter.tsx:181-205).

*   **Very constrained.** Since [discipline doesn't scale](https://www.sicpers.info/2020/10/discipline-doesnt-scale/). Inspired by Golang. Should assume the developer is an _unexperienced, lazy, (immediately) forgetful, and habitual_ creature. As long as software development is done by mere humans. This assumption sets the bar (the worst case), and is a good principle for DX, as well as UX. The constrained nature of the language should allow for quick learning and proficiency. Complexity should lie in the system and domain, not the language. When the language restricts what _can_ be done, it's easier to understand what _was_ done (a smaller space of possibilities reduces ambiguity and increases predictability, which gives speed for everyone, at a small initial learning cost). No _alias names_ in the language, except for in documentation. Inspired by [Python](https://www.python.org/dev/peps/pep-0020/) (explicit over implicit, one way over multiple ways). Counter-inspired by [Perl](http://www.wall.org/~larry/pm.html) (postmodern plurality) and _aliasing_ in the [Ramda](https://github.com/ramda/ramda/issues/297) library. The language should favor one consistent vocabulary, since it increases predictability and reduces variability. Names should not mimic any other language per se, but attempt to cater to complete beginners, because [notation has a large impact on novices](https://quorumlanguage.com/evidence.html), a principle inspired by [Quorum](https://quorumlanguage.com/).

    *   "_`<insert your favorite programming paradigm here>` works extremely well if used correctly."_ as [Willy Schott said](https://medium.com/@willy.schott/one-can-say-the-same-about-fp-ca7b3944a32b). The ideal programming language should both **work extremely well even when used incorrectly** (which all powerful tools will be), but first and foremost **be extremely hard to use incorrectly**.
    *   Not overly terse. Counter-inspired by C. Maybe give compiler warnings if the programmer writes names with less than about 4 characters. **Reading >>> writing**, since time spent reading is well over 10x time spent writing (inspired by [Robert C. Martin](https://www.goodreads.com/quotes/835238-indeed-the-ratio-of-time-spent-reading-versus-writing-is)), and writing can be alleviated with auto-complete, [text macro expansions](https://www.trankynam.com/atext/), and [snippets](https://code.visualstudio.com/docs/editor/userdefinedsnippets), in the IDE.
    *   No runtime reflection. Counter-inspired by meta-programming and runtime type inspection in Ruby.
    *   Not overly verbose. Counter-inspired by XML and Java. Maybe compiler warnings if the programmer writes names with more than about 20 characters.
    *   [The Rule of Least Power](https://www.w3.org/2001/tag/doc/leastPower.html) (by WC3), suggest a language should be the least powerful language still suited for its purpose. To minimise its complexity and surface-area. For better reuse, but more importantly: to make programs, data, and (I will include) _data flows_, easier to analyse and predict. Inspired by [FSM](https://en.wikipedia.org/wiki/Finite-state_machine) & [XState](https://xstate.js.org/). It needs, however, to be just powerful enough to be generally useful (and not limited to a DSL). Possibly Turing-complete. Given these considerations, a Lisp-style language comes to mind. But there's reasons Lisp never became hugely popular. My guess: readability. So while it could be a Lisp-language (or compile to one), it should read better than one.
    *   It should be small, but extensible by using simple primitives. Preferably, the language should be [self-hosting](https://github.com/nagydani/seedling/blob/master/RATIONALE.md#self-hosting), but if not, then probably built using [OCaml](https://qr.ae/pGz7Uh), [Rust](https://news.ycombinator.com/item?id=24224837), [Racket](%5BRacket%5D(https://felleisen.org/matthias/manifesto/sec_pl-pl.html)) or [maybe](https://qr.ae/pGz7Og) Haskell (LLVM has bindings to these languages). Should do more with less. Inspired by Lisp. Since predictability is good for humans reading, and for machines interpreting, and if it's predictable to machines, humans also benefit. **Important**: _"As one adds features to a language, it ramps up the complexity of the interpreter. The complexity of an analyzer rises in tandem."_ - [Matt Might, on static analysis](http://matt.might.net/articles/intro-static-analysis/)
    *   **Code-Formatter**, like [gofmt](https://go.dev/blog/gofmt), inspired by Golang. A tool to auto-format code into a standard. Since standardisation creates readability and faster onboarding of new developers. It also enables mechanical source transformation, which is crucial for language evolvability.
*   Containability and explicitness. Inspired by **pure functions**. Perhaps the language should even restrict a function's scope only to what's sent in through its parameters. So no one can reference [hidden inputs](http://blog.jenkster.com/2015/12/what-is-functional-programming.html) (i.e. side-causes). Thus enforcing more predictable functions, where it is always apparent where it is used: what the function takes in and what it returns. So to achieve partial application of functions (i.e. useful closures), without addressing the outer scope implicitly, could be to supply constants from the outer scope as default/preset/front-loaded parameters. Since "explicit is better than implicit" (inspired by [Python's principles](https://www.python.org/dev/peps/pep-0020/)). That way, they would be declared in the function signature, so you don't have to dive into the function to discover/investigate them. With the added benefit that the function could be customized by the caller through overriding the defaults.

    *   Memoization automatically. Aided by pure functions. The programmer shouldn't have to think about [memoization](https://en.wikipedia.org/wiki/Memoization) when programming, but should be able to tune the degree of memoization (since it is a space/time tradeoff) through general configuration, for advanced cases not optimal from the default. Run time optimisations such as these are not critical features, but certainly nice to have, and should be considered in the language design.
*   Pattern-matching. Inspired by Elixir. The expression-oriented nature of the language should make this natural, without extra/fancy syntax.

*   Not file boundary dependent. Can be split into files, but execution shouldn't be dependent on file boundaries. So the programmer is free to keep code tightly together. Inspired by SolidJS.

*   No magic / hidden control. Control-flow should be easy to trace, because it makes it easy to understand and debug. Less magic. Counter-inspired by Ruby on Rails. Inspired by Elixir [Phoenix routing / endpoint plugs](https://dockyard.com/blog/2015/11/18/phoenix-is-not-rails). Testing isolated parts is made possible by explicitness. Explicit is better than implicit. Inspired by [Python's principle](https://www.python.org/dev/peps/pep-0020/). Explicitness makes testing isolated parts of the system possible. So Explicit > Implicit, almost always. (Although implicitness is preferred when one may intuitively and robustly determine the convention through the context. E.g. Needing `self.` references to access class variables inside the class methods would just be [noise](https://stackoverflow.com/q/68461184/380607). This is counter-inspired by Python, and inspired by Ruby. However, using `self` and `this` are considered [an anti-pattern](https://kentcdodds.com/blog/classes-complexity-and-functional-programming) in general.)

    *   Make Inversion of Control (IoC) hard/impossible (?). Should ideally always return control to the programmer, not take it away. To enable the programmer to always follow the control-flow by simply reading and following references. Thus, `yield` should also be avoided (counter-inspired by Ruby). Problem: Could make domain code dependent on integrations which goes against [the dependency inversion rule](https://khalilstemmler.com/articles/enterprise-typescript-nodejs/clean-nodejs-architecture/). So other patterns, like containing integration coupling in an intermediary abstraction ('port/adapter' function or library, or 'channels'), would need to be developed.
    *   Libraries over frameworks, as a [strongly recommended](http://tomasp.net/blog/2015/library-frameworks/) community convention (because frameworks cannot be prevented by a language, afaik). Frameworks utilise inversion of control. That creates Stack Traces which are really hard to debug, because they reference the framework and not your own code, [esp. problematic with concurrency](https://youtu.be/PNx9WqQ9QeA?t=646). And when yielding control to various (micro-) frameworks, compatibility becomes a specific issue. The programmer shouldn't ever have to ask: "Is this library/framework compatible with this other one?". Counter-inspired by JS ("JS Fatigue"). Or have to ask "Where is the execution path of this program?". Counter-inspired by the magic of Ruby on Rails. When the control is always returned to the programmer (no IoC), he/she may likely mix and match more as pleased, without up-front worrying about compatibility (leading to analysis paralysis).
    *   Meta-programming: No first-class macros (runtime), since it is a too powerful footgun. But should have compile-time macros. Inspired by Clojure. So that the language can be extended by the community, and so that legacy code could be updated to latest language version by processing the code with macros to transform the syntax.
    *   **Expressions over statements**. The calling code should always get something back ([Is. 55:11](https://biblehub.com/isaiah/55-11.htm)). Because the returned object can be further chained. Inspired by Clojure [and Haskell](https://blog.jle.im/entry/first-class-statements.html). Counter-inspired by JavaScript. [Statements suck](https://bugzilla.mozilla.org/show_bug.cgi?id=513379#c21), as even Brendan Eich, the inventor of JS, admits. A goal should be to eliminate the subjective/anthropocentric bias that afflicts programming (especially the Imperative kind), because: _It is not you, the programmer, which is, or should be, calling code, but code should be calling code (and not terminating in the void, like as if it's you the programmer who is acting on the machine)._
*   Abstractions which are powerful, made from simple primitives. _Maybe_ homoiconicity... since it would make writing the compiler easier, and making the language more readily available to evolve in the community on its own (permissionless). Inspired by Lisp and Clojure's Rich Hickey.

    *   But this would allow meta-programming, and the associated complexity..?
    *   The language should maybe also not be so powerful that programs become entirely composed by very high-level domain-specific abstractions, since it encourages esotericity and sociolects, but most importantly: code indirection when reading/browsing. Coding should not feel like designing an AST, so should try to encourage keeping the code flattened (by piping perhaps?) and as down-to-earth as possible. Could maybe be alleviated by an IDE plugin which would allow temporary automatic code inlining (editable previews).
*   **Reversible debugging / time-travel debugging (TTD)**. “Reverse debugging is the ability of a debugger to stop after a failure in a program has been observed and go back into the history of the execution to uncover the reason for the failure.�? [Jakob Engblom](https://undo.io/resources/reverse-debugging-time-travel-debugging-brief-hist/). Inspired by [Elm](https://www.youtube.com/watch?v=Agu6jipKfYw). Re: Accounting for human limitations and affording the most natural way of thinking: _"The problem you are trying to fix is at the end of a trail of breadcrumbs in the program’s execution history. You know the endpoint but you need to find where the beginning is, so working backwards is the logical approach."_ [source](https://undo.io/resources/reverse-debugging-time-travel-debugging-brief-hist/). Should at least have this. Could be enabled by, but not necessarily need:

    *   Reversible / invertible control flow: "A reversible programming language produces code that can be stopped at any point, reversed to any point and executed again. Every state change can be undone." [source](https://wiki.c2.com/?ReversibleProgrammingLanguage). Maybe. Might not be feasible, or desirable, when it comes down to it. Might be aided by immutability, and persistent data structures (if they are extended with history-traversal / operation logging features, in addition to structural sharing).
*   **Transpiler, configurable**, so it could translate between all language dialects and variations. So that the language could evolve in multiple directions, and consolidate later, without harm.

    *   Homoiconicity could perhaps enable this.
*   **Async: blocking/sync interface, but non-blocking I/O**. Inspired by Golang, and to lesser extent JS / Node.js too. Should not have to litter code with async/await repeatedly (see: [what color is your function?](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/) and [the problem with function annotations](http://pchiusano.blogspot.com/2009/05/optional-laziness-doesnt-quite-cut-it.html), and [async everything](http://joeduffyblog.com/2015/11/19/asynchronous-everything/)). NB: But hiding the async nature with synchronous seeming abstractions could create a dangerous model-code gap with a potential impedance-mismatch and cause for design errors and bugs ([inspired by Simon Brown](https://youtu.be/5OjqD-ow8GE?t=2768)). So the language should make some abstractions around async simple (like channels and goroutines in Golang). But also inspired by declarative and easily statically analysable async contexts, made with JSX, like Suspense (async if-statement), in React and SolidJS.

    *   **Ease of reasonability is first priority**, and I believe it is best afforded by **simple and clear abstractions** (without model/code impedance mismatch, as made important by failures of ORM's and distributed contexts). The choice of sync interface here as opposed to async, is similar to how the wish for [lazy evaluation](https://en.wikipedia.org/wiki/Lazy_evaluation) by default was discarded for eager evaluation by default. One [argument by Ryan Dahl of Node.js](https://mappingthejourney.com/single-post/2017/08/31/episode-8-interview-with-ryan-dahl-creator-of-nodejs/) is that sync by default with explicit async (he mentiones goroutines in Go) is a nicer programming model than async everything (like in Node). Because it's easier to think through what you're doing than jumping into other function calls like in Node.js. Reasonability is a top priority.
    *   [Rich Hickey also has some good arguments against async by default](https://youtu.be/drmNlZVkUeE?t=1845) (when implemented with callbacks), namely that it:
        *   fragments your logic (spread out into handlers), instead of keeping it together. Programmer has to deal with multiple contexts at once (complicated), instead of one overarching context (simple).
        *   callback handlers perform some action once in the future, but the state they are operating on may have mutated in the meanwhile. So it may give a false confidence in being able to get back to the state as it were when the callback was made. Want to avoid the dreaded [Shared Mutable State](https://youtu.be/PNx9WqQ9QeA?t=90). May be solved with only allowing immutable constructs.
    *   On the other hand, having sync by default, and _async through [Channels](https://tour.golang.org/concurrency/2)_:
        *   gives the control back immediately (in line with functional composition) instead of functions that effectively evoke side-effects on the real world on the other end (as callback handlers do). In line with our principle: Always give control back to the programmer.
        *   channels are generalized pieces of code that can handle many connections (pub/sub).
        *   channels afford safe concurrency (thread handling), whilst with callback handlers (unless used in an event-loop system such as JS) the programmer has to ensure safe concurrency (which we [don't want](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/)).
        *   channels afford choice on when to handle an event, whereas with a callback it gets called whenever it gets called (event-loop). Channels work in line with our principle: Always give control back to the programmer.
    *   All of the above have implications for reasonability. Needs to be investigated further... Golang's way of handling async seems to be [the current gold standard](https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/), touted by many bright people, since _"Golang has eliminated the distinction between synchronous and asynchronous code"_ (by letting the programmer code everything in a sync fashion, but doing async I/O under the hood). Golang's principle of _"Don't communicate by sharing memory; share memory by communicating."_ avoids the dreaded [Shared Mutable State](https://youtu.be/PNx9WqQ9QeA?t=90) and affords itself better to ensure **simple, safe, and scalable modes of thinking** (our core principle): It's hard to think of something, if it has changed the next time you think about it (thus: immutability). Or if thinking about it changes it (manifesting in code the cognitive equivalent of [Heisenbug](https://en.wikipedia.org/wiki/Heisenbug)'s): Programmers need to be able to reason about a program's state without simultaneously modifying that state ([inspired by CQRS](https://en.wikipedia.org/wiki/Command%E2%80%93query_separation#Connection_with_design_by_contract)).
*   **Concurrency. For Multi-Core and Distributed.** Probably a CSP model, or a similar or novel model, due to easier to [debug concurrency](https://youtu.be/PNx9WqQ9QeA?t=646). Inspired by Go.

    *   Async: Concurrency should integrate well with the async feature of the language. Default should be to easily ship tasks off to be completed elsewhere (other thread/process/worker/server). Inspired by Golang and JS.
    *   Probably not implemented as an Actor Model. Since sending events may be harder to reason about than stricter promise-based operations (using callbacks under-the-hood). Counter-inspired by StimulusJS. Inspired by ReactJS.
*   **Scalable**: From single core to multiple core CPUs, and from one to a distributed set of machines. Inspired by the purpose of the [Actor Model](https://www.brianstorti.com/the-actor-model/), from Erlang/Elixir. But rather implemented with "Machines" which is a novel concept that combines a Mailbox with a stateless function (executed asynchronously), as the universal primitive of concurrent operation. That way, instead of functions calling functions directly, which strongly couples them, which is bad, they call each other by sending Messages (containing the parameters) to the other function's Mailbox. We call such functions "Machines". Each of them are in fact a mini-computer, or a computer-within-the-computer, if you will. Such Machines should be able to be moved to distributed systems without rewriting the code (inspired by Actor Model systems).

    *   Ideally, for performance, when code is compiled to be run on a single machine, the compiler should be able to be optimise away the Mailboxes, so that Machines can be turned into (simpler and faster) synchronously executed functions.
    *   The language should facilitate and nudge developers's towards creating "Functional Core, Imperative Shell" architectures (inspired by [Bernhardt at 31:56 in his Boundaries talk](https://www.destroyallsoftware.com/talks/boundaries)), to preserve the purity of functions as far as possible, while also **containing side-effects**:
        *   By semantic rules: A function should _either return a value, or don't return anything_ (i.e. be simply a void procedure). And a procedure can never be placed within a function.
        *   Alternatively: use [an IO action of an IO type](https://blog.jle.im/entry/io-monad-considered-harmful) (inaccurately named "IO Monad" at 30:44 in the [Boundaries talk](https://www.destroyallsoftware.com/talks/boundaries)), transparently (without actually having to [deal with the concept of a Monad](https://www.google.com/search?q=what+is+a+monad+meme&client=firefox-b-e&source=lnms&tbm=isch&sa=X&ved=2ahUKEwjdu9i0ocXyAhWZS_EDHdiqDZcQ_AUoAXoECAEQAw&biw=1512&bih=833)). Where you effectively construct a sequence of I/O operations to be executed later. Inspired by Haskell. Something like this is needed because the Mailbox is stateful (it is constructive/destructive, like a queue), and I/O messaging would be a side-effect. The Machine/Mailbox is inspired by the Actor Model from Erlang.
        *   Alternative to use of Monads and Immutability: Use [Uniqueness Type](http://lambda-the-ultimate.org/node/2708), which allows mutability and pass-by-value while also preserving referential transparency (since side-effects are ok in a pure language as long as variables are never used more than once). Inspired by [Clean](https://en.wikipedia.org/wiki/Clean_(programming_language)) and [Idris](https://www.idris-lang.org/). Possibly use [Simplified Uniqueness Typing](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.142.5413&rep=rep1&type=pdf), inspired by [Morrow](https://www.openhub.net/p/12489).
*   Reactive. Inspired by [Functional Reactive Programming, and Elm](https://www.youtube.com/watch?v=Agu6jipKfYw), and [The Reactive Manifesto](https://www.reactivemanifesto.org/). Though the latter is geared at distributed systems, it could also be a model for local computation (rf. Actor model, and [Akka](https://akka.io/)). The programming language should make default and implicit the features of reactivity and streaming, as opposed to preloading and batch processing. (Reactive Streaming Data: Asynchronous non-blocking stream processing with backpressure.)

*   No single-threaded event loop that can block the main thread. Counter-inspired by JS.

*   [Transducers](https://medium.com/javascript-scene/transducers-efficient-data-processing-pipelines-in-javascript-7985330fe73d), under-the-hood, to compose and collate/reduce transformation functions (chains of map, filter etc. turn into a single function, [visualised here](https://medium.com/@roman01la/understanding-transducers-in-javascript-3500d3bd9624)). Chaining function calls should use language-supported transducers implicitly. Language should not require special `compose` syntax.

*   Eager evaluation, generally. Since it is more straightforward to reason about in most cases, simpler to analyze/monitor, and spreads memory consumption out more in time, than [lazy evaluation](https://en.wikipedia.org/wiki/Lazy_evaluation) which would pile up work and in worst case could [overflow memory at an unexpected time](https://prog21.dadgum.com/40.html) (in any case, the programmer shouldn't have to worry about evaluation strategies, including [space usage performance and evaluation stack usage](http://pchiusano.blogspot.com/2011/12/future-of-programming.html)). But should use the more efficient lazy evaluation when currying functions or chaining methods, unless intermediate error-handling or similar requires value realization (and even here, transducers could potentially alleviate unnecessary value realization). Inspired by Lazy.js. But this is an optimisation that could wait. Concurrent operations across threads/processes should never be lazy. Counter-inspired by Haskell.

*   Gradually typed, as types can be boilerplate and create noise in the code (counter-inspired by TypeScript, and inspired by Elm). Most types should be inferred (inspired by Haskell and TypeScript).

    *   No runtime type errors. Inspired by Elm (and Haskell). See 'Error Handling & Nullability'.
    *   Types should be associative/commutative/composable/symmetric, inspired by Dotty/Scala3, and the ['Maybe Not' talk by Rich Hickey](https://youtu.be/YR5WdGrpoug?t=623).
    *   Types should be enforced statically at program exit boundaries (so external libraries or outgoing I/O are ensured existing typings).
    *   Strongly typed (checked at compile time), not weakly typed, since implicit type coercion (at runtime) can be unpredictable. Inspired by TypeScript. Counter-inspired by JavaScript.
    *   Structurally typed (inspired by TypeScript, OCaml), instead of nominally typed (counter-inspired by [Java](https://www.youtube.com/watch?v=aSEQfqNYNAc) and [Haskell](https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-type-systems-are-not-inherently-more-open/#footnote-ref-2-1)).
    *   No [generics](https://research.swtch.com/generic), but static analysis of functions based on structural typing on the partial structure used (anywhere) inside it (disregarding dynamic/conditional use). If passing in dynamic values to a function, type would need to be statically declared _where it is passed in_, so static analysis at compile time can determine if those types match the partial structural types used within.
    *   Type inference, fully decidable. Inspired by OCaml. For increased readability and convenience (though not essential, cf. popularity of Rust). To not have to declare types everywhere. But local type inference for the body of functions are what's most important.
*   **Composable.** Favour composition over inheritance. Inspired by Robert C. Martin, Martin Fowler, and [JSX in React](https://reactjs.org/docs/composition-vs-inheritance.html). Composability entails it should be easy to write code that is _declarative_, _isolated_ and _order-independent_. See "strongly typed".

*   Memory safe, ergonomic, and fast. should be safely and implicitly handled by the language, without a runtime GC.

    *   No Garbage-Collector (GC), but also no garbage. Deterministic Object lifetimes, and Ownership tracking (affinity type system). Inspired by Rust and [Carp](https://github.com/carp-lang/Carp).
    *   Memory-safe. Maybe a Borrow Checker and Reference Counting. Inspired by Rust. But ideally, [Ownership and Borrowing](https://blog.logrocket.com/introducing-the-rust-borrow-checker/) should be implicit by the programming language, so the programmer wouldn't have to think about low-level concerns such as memory management. To avoid conceptual overhead of manual memory management (as with explicit borrowing semantics), the language should perhaps use or take inspiration from [Koka's Perceus Optimized Reference Counting](https://koka-lang.github.io/koka/doc/book.html#why-perceus). Koka apparently allows even more [precise reference counting (see sect: 2.2)](https://www.microsoft.com/en-us/research/publication/perceus-garbage-free-reference-counting-with-reuse/) than Rust.
*   Secure from the start. Secure runtime. Inspired by Deno. Safety has to be a built-in design-goal from the start, it cannot be added on later. As evidenced by the justification of existence of Deno (Node was unsafe), and Rust (C++ was unsafe). Also, see: memory safe.

*   Few core primitives, and based on very few fundamental concepts to learn. Inspired by Lisp. No limiting distinctions like only half-way interchangeable expressions vs. statements in JS.

    *   But avoid mini-languages/DSL's. Dialects/sociolects hinder generalised understanding and learnability (adds knowledge debt). Counter-inspired by Lisp and Ruby. Even though it might be true that “Domain-Specific Languages are the ultimate abstractions.�? as Paul Hudak put it in 1998, some cross-domain terms are usually helpful for onboarding programmers, since they afford familiar knobs on which to hang the other unfamiliar code. Even if you don't understand the domain (or its plethora of abstractions), you would at least understand _something_. From where you could build your further understanding.
*   Ergonomic to type. Prefer text over special characters like curly brackets (they are hard to tell apart from parentheses in JS). No littering of parentheses. Inspired by Ruby. Counter-inspired by JavaScript, Lisp, and JSON.

*   No super-powerful tools which may hurt you or others in the long run. Counter-inspired by meta-programming in Ruby.

*   Crash-safe. Can crash at any time and resume computation at exact same spot when restarted. Inspired by Erlang.

*   Piping, or some form of it. But always top-to-bottom or left-to-right. Inspired by Bash, and functional programming with pipes (Elixir, BuckleScript, and [ts-belt](https://mobily.github.io/ts-belt/)). [Data-first instead of data-last](https://www.javierchavarri.com/data-first-and-data-last-a-comparison/).

*   No Exceptions. Inspired by Go. But Recoverable and Unrecoverable errors. Inspired by Rust. (Definitely [no checked exceptions](http://literatejava.com/exceptions/checked-exceptions-javas-biggest-mistake/), as it breaks encapsulation by imposing behavior on the callee. Counter-inspired by Java).

    *   Result data type, for error handling and validation. Inspired by Result from Rust and F#, and [Either from Haskell](https://blog.thomasheartman.com/posts/haskells-maybe-and-either-types) and Elm, although it should not be called Either, as [Either is a confusing misnomer](https://youtu.be/YR5WdGrpoug?t=623).
*   Error handling & Nullability: No explicit `null` or `nil` value. Meaning no Null Errors (typically occurring far removed from their point of inception). Inspired by Elm and Rust. But without having to explicitly declare `Maybe` or `Option` types (inspired by Hickey's [Maybe Not](https://youtu.be/YR5WdGrpoug?t=623)). Instead, _automatically but statically_ infer and create/augment a function's return type to a "nullable reference type" indicated by a `?` after the typename, whenever there is an unhandled condition that could result in a null value. Or _automatically_ create a `NullObject` (see: [NullObject pattern](https://youtu.be/OMPfEXIlTVE?t=904)) of the function's declared return type (which with type inference can avoid some timid coding patterns, like always checking for null, counter-inspired by Golang). Maybe even better, [let every type declare and handle their own empty state](https://twitter.com/magnemg/status/1417517013321273345?s=20). If all types are [defined in terms of Monoids](https://twitter.com/magnemg/status/1429914312211054602?s=20), then **_null_ can be replaced by the identity value (of each Monoid)**, so that combinations within that type never fail, and never alter the result. Resulting in no more [timid coding patterns](https://youtu.be/T8J0j2xJFgQ?t=151) like null checks. Furthermore, the return type from functions using I/O (like `IOMonad` in Haskell), should always be augmented/inferred from static analysis.

    *   _Variant Types_ for error-handling using return values (like `Result<Type, Error>`, inspired by Rust), instead of special syntax. [Counter-inspired by Golang](https://qr.ae/pGPMUk).
    *   So that [you have less avenues to explore when debugging](https://overreacted.io/the-bug-o-notation/) and [fewer branches to check when programming](https://lispcast.com/why-are-corner-cases-the-devil-%f0%9f%98%88/), so you can write [Confident Code](https://www.youtube.com/watch?v=T8J0j2xJFgQ) focused on the happy-path.
    *   No possibility of failing silently during runtime (due to syntax errors). Counter-inspired by JS.
*   Compilation should be able to target some popular language & ecosystem, like **transpile to JavaScript or [compile to WASM](https://dev.to/taybenlor/web-assembly-should-be-a-default-binary-target-44m6)**, or potentially even the JVM, to get cross-platform interoperability. But not any target for any cost, if it would put unwieldy constraints on the language design. WASM seems like the best candidate.

*   Small standard library. To have some common ground of consolidation, and to provide the basic and most common utils. So usage will be fairly standard, and coming into a new codebase not feel too foreign.

*   Single package directory: Some sort of singular reference to a library package information service. So the community can organise around one common point, instead of scattering. Inspired by NPM. But doesn't necessarily need to be centralised package download/storage, the storage/download could be decentralised. But would need to be safe. Cert signing?

*   Runtime environment: Be able to run on some existing popular cross-platform runtime (like WASM or the JVM?). Inspired by Clojure. And/Or have a very minimal programming language runtime (without a GC). Inspired by Rust. But the runtime should in any case handle the scheduling of goroutines, inspired by Go.

*   **Ecosystem: Interoperable** with one or more existing programming language ecosystems. To import or reuse libraries. Without too much ceremony. So the ecosystem doesn't have to start from scratch.

*   Be [general purpose enough](https://github.com/nagydani/seedling/blob/master/RATIONALE.md) to at least write scripts and CLIs, but also web servers/clients.

*   Editor integration: Should afford simple integration into editors/IDEs like VS Code. Syntax highlighting, a [language server](https://code.visualstudio.com/api/language-extensions/language-server-extension-guide) (for autocomplete, error-checking (diagnostics), jump-to-definition etc.), via the [Language Server Protocol (LSP)](https://microsoft.github.io/language-server-protocol/).

*   Well-documented. Documentation on language syntax should be accessible from the editor/IDE, vha. the LSP.

*   Well-tested.

*   All the while, the language should avoid the [fate of the Vasa](https://www.youtube.com/watch?v=ltCgzYcpFUI).😂 Which means a feature creep resistant _core language_. (I am aware the irony of this feature list, but read on...) Which should be designed and decided upon as early as possible (when the degrees of freedom in the design space is as wide as possible), with a holistic view. Boring > clever. Designed to reach a 80% [sweet spot of most important features](https://youtu.be/ltCgzYcpFUI?t=2244), foregoing the most exotic and esoteric features, and foregoing the ability to solve edge-cases (such should be relegated to interoperability with other more specialized programming languages). Since 80% of the work and complexity would come from the last 20% ([The Pareto Principle](https://www.investopedia.com/terms/1/80-20-rule.asp)).

One or more of these requirements might be conflicting / mutually exclusive. Maybe. But maybe not?

One can always dream.

This is a list of my preferences. Some would probably be quite controversial. Like my dislike for certain features, which a lot of other people like (e.g. meta-programming). I might just not be familiar enough with them to have developed an appreciation for them.

I will try to keep this list updated if and when I change my mind on any point, which I am open to doing. I have already changed my mind from negative to positive on pattern-matching.

What features (or lacking features) would your dream programming language have?