| This is an interesting approach. I'm working on a Lisp variant that recognizes the difference between expressions and... non-expressions? But taking the opposite approach: I found a way to make it so that everything is an expression while allowing one to do everything you would do with a non-expression via expressions. To achieve this, there are two kinds of expression: mutations and functions. There are two things to understand about this: 1. All expressions take in the environment. Most functions don't use it, while most mutations do. 2. All expressions run inside a trampoline that evaluates them. The difference between a mutation and a function is that when the trampoline evaluates a function, it places its result into the return register (where it can be picked up by something else). In contrast, when the trampoline evaluates a mutation, it replaces the environment with the result. This is why mutations typically use the environment--rather than destroying the environment, you usually want to build the new environment with most of the old environment. Some examples: ((mut () env (assoc env :foo 1))) ; equivalent to (define foo 1)
((mut () env
(assoc env :my-define (mut (dest src) env (assoc env dest src)))))
; this is actually how `define` is defined
((mut () env (map)))
((+ 1 1)) ; throws exception "undefined symbol +" because previous line emptied the environment
The "everything takes env" bit is inspired by J. Shutt's paper on his Kernel programming language: https://www.wpi.edu/Pubs/ETD/Available/etd-090110-124904/unr... and a lot of what I'm working on is built on his work. |