Dontuse is an experimental modular metaprogramming[1] language. You can think of it as another toy Lisp. Since it is experimental, it runs in Racket.[3]
The first thing that you might notice is that it has fewer parentheses than Lisp. Dontuse does not need parentheses for built-ins. That makes it more like other programming languages. I plan to go back to Lisp style syntax for myself. I consider it a successful experiment though. This syntax uses FREEZE and THAW in place of QUOTE and EVAL. Also, lists are self evaluating.
For the memory model of Dontuse, all pairs are lists. I think that is an easy win over traditional Lisp.
A big part of programming is to to manage side-effects. Dontuse segregates side-effects into STATE expressions. They have their own memory, so you can think of them as an embeded language / DSL. (Dontuse is split into functional and imperative sub-languages.) LOAD and STORE statements work like dynamic parameter passing. All IO is via EVENT statements. Two way movement of info is powerful and lets the programmer ruin a good thing. (Contrast that with Haskell and Rust.) It is a big change from Lisp and, I believe, a big improvement.
Static typing has the advantage of modularity, but it is brittle. There is a consensus that adding some flexability, via sub-types (OOP), is a big improvement over static typing. What if we try to add some modularity to dynamic typeing? I'm not the first to think of that, but the atempts that I know of (such as CLOS and MOP) are much more complicated than they need to be.[2] Encapsulation is done via BOUNDARY expressions. They can be thought of as anonymous types.
1 I use meta-programming loosely. You might think of it as my attempt to extract what is good from the Lisp family of programming languages. (Language design starts off ambitiously / optimistically.) I think that it should be possible to improve on a programming language that has kept a core design from the 1950s.
2 User defined types first appeared in staticly typed languages. I believe that was because of their brittleness. Those type systems have been influential. Intuitively, the different workflow that those languages have, need different features in a type system. If we deemphisize metaprogramming, then we might as well switch to Julia (see also Dylan, Lunar).
3 Using an underlying implementation's error system helps the user make changes. The reader / parser code limits it to Racket.
Download the files to the same folder / directory. Rename the files without '.txt'. Open du-main.scm and run it. There is some built in help.
du-main.scm the interpreter
du-reader.scm scanner and parser
du-operations.scm the bulk of operations / instructions
du-other.scm boundary, state, suggest
du-core.scm eval, apply, and thunks
du-environment.scm the store of variables (name bindings)
du-utility.scm tables (including help)
There are many potential improvements / experiments:
multi line input
scope rules in STATE (The main environment takes president sometimes.)
switch to normal Lisp syntax / more parenthesis
improve FREEZE and THAW (as long as we use the current syntax)
labels as first class values, but only in STATE (would need syntax)
many different semantics for STATE
Here's an example of no particular importance. Build 30 wants it all on one line, if you want to try it yourself.
name secret number imp 8
state
(
(
label start
event basic-output message extra
event basic-input guess extra
store guess guess
jump ify is-equal-p guess secret
string imp "end"
string imp "start"
)
(
label end
event basic-output success-message extra
)
)
(
guess number imp 1
message string imp "guess again"
success-message string imp "You got it!"
)