I created an experimental programming language. It is inspired by Lisp. People love Lisp. People hate Lisp. People are confused by Lisp. Lisp is stands out as an old and distinctive programming language. It is a good case study for programming language design.
I feel a need for encapsulation, so that was the first modification I was sure I wanted. Adding too many restrictions will disrupt the Lisp style of programming. Being able to access data (no information hiding) enables continued modification of code / developing complex systems. A minimal encapsulation boundary is the similar to an unnamed user defined type (also works like a capability). By limiting encapsulation to a subexpression, it can be removed when needed. The BOUNDARY form takes two lists of functions: Producers return the typed value and consumers return a normal value. Other functions produce an error while attempting to access the value.
We can achieve an expressive language with a clean design (coherent, consistent, concise, ?) by splitting the language into imperative and functional parts. Explicitly switching between imperative and functional programming has the user focus on the kind of modularity that is needed. Note, imperative (statement based) programming corresponds to a semantic topology, while functional (expression based) programming corresponds to a syntactic topology. The SEQUENCE form takes a list of statements and an initialization list of alternating names and values.
I am embarrassed by how long it took me to realize: Lisp really is about lists. We do not need non-list pairs. Requiring the user to explicitly check S-expressions is not worth saving one cons-cell. (This is my only Lisp specific modification.)
Let's use syntactic placeholders for subtypes and other language extensions. Be warned that the utility of placeholders is only fully realized latter, when something is put in the place. I had numerous encounters, where, seemingly simple things broke standards compliance. While I was annoyed with compiler writers for doing that, we could avoid those problems with a mere placeholder. A specific need for extensions is to extend base / primitive types. Long before Unicode, IEEE developed standards for numbers used in CPUs. Not many people foresaw that there would be a demand for low precision numbers (see 'deep learning'). Not only did compiler need to be modified, but the language standards had to be modified as well. I am a little sad that nobody seems to think of that as a problem computer science should solve. The forms: EVENT statements do IO, SUGGEST expressions can be more detailed than compiler flags, IMPORT / EXPORT pick your reuse, LITERAL isolate parsing and enable abstract primitive types (can use a tool to hide the extra syntax / typing)
While we are considering language elements that don't seem to do much, consider a syntactic naming convention. We reserved a symbol. We do not allow that symbol to be used in a variable names, except via a special form. Thereby, we provide the scaffolding for naming conventions. It should be particularly helpful for Lisp. Name-spaces are imperative (do not maintain syntactic locality). Named user defined types also form naming hierarchies (, but do not maintain syntactic locality).
Many Lisp proponents emphasize the use of DSLs for APIs. It is ironic that Lisp does not provide many parsers. A homo-iconic syntax is useful for code generation (macros, instrumenting, minimizing special forms, etc.), but is inconvenient otherwise (read and write code). If we restrict our syntax, then we can use a reversible syntax. Not yet available.
Use a backward scope rule. When we write code that maintains syntactic locality (functional), we thread state though function returns. We can ease this burden with a scope rule that uses static dependency as a hierarchy, a bit like lexical scoping. This causes issues for garbage collection. We may want to stage execution for that reason. Using a special form / syntax helps avoid confusion over the scope rules. Not yet available.
And, or, and not do not accept strings, numbers, pairs, procedures, or symbols.
Everybody thinks their ideas are good. I hope that these will be useful to someone.
The interpreter can be run in Racket or MIT-Scheme. Just adjust the comments accordingly. The python code should be easier to understand, but is just meant to illustrate the main ideas. See plis.py for a simple scheme interpreter (writen in python) to play with. You can check out scheme.org if you are not familiar with the scheme programing language.
du-REPL.scm the interpreterControl structures func ify eval apply Compound data list append } lists or strings index slice length Predicates is-equal-p is-empty-p is-name-p is-less-p \ numbers or is-greater-p / strings is-list-p is-logic-p is-number-p is-string-p Basic data operations and or not add subtract multiply divide expand append repeate from lists Language extension literal subtypes and syntax suggest expressions export \ REUSE import / event statements Name valued (substitution) func name boundry encapsulation / un-named user-defined-types sign built-in Naming convention <- un-named / functional namespaces sequence Separated side effects <- imperative DSL in a functional PL export import Statements: now (assignment) latter (copy into environment) forget (delete name binding) table \ insert \ delete \ update > list o lists search / reg-fkey / delist-fkey / loop (while loop) \ structured program theorem case (guarded block of code) / Bohm-Jacopini def-proc (copy in, copy out parameter passing) \ limited reuse use-proc (use local memory) / (abstracts addresses) EVENT (NOP – outside language standard – user defined)