Hybrid Environments

The Challenge
Make a language... My solution is a combination of far too many quotes, and something I call "hydrid environments". I made a minimal interpreter to demonstrate the ideas in javascript. If nothing else, it's kinda fun.
How to run the code snippets Things that are bad
Basics
It's a little lispy. 'noop' is a variadic function that just returns. This is useful for doing multiple things in sequence, because arguments are reduced strictly in order before being passed to operators/functions.
(noop (println (+ 1 2)) (println (+ (println "First") (println "Second"))) )
Output

Result
Quotes
Quotes let us treat code as data and data as code. They're used a lot in this language. They're best thought of as objects that contain code and reduce to themselves, rather than a special form or a mechanism for delaying intepretation. Printing things can be uninformative, but the 'json' function sometimes helps.
(noop (println (+ 1 2)) (println '(+ 1 2)) (println (json '(+ 1 2))) )
Output

Result
Hooks
Hooks are another type of thing. They are: The interpreter itself is just a function in the language called "run". It takes two arguments, a quote expression and an environment, and returns the result of reducing the expression in that environment. So, (run '@run nv) == run, and (run '@nv nv) == nv, for any nv.
(noop (println @run) (println @nv) (println (@run '(+ 1 2) @nv)) (println (== (@run '@run @nv) @run)) (println (== (@run '@nv @nv) @nv)) )
Output

Result
Closures
Closures are what you'd think of as lambdas, but take their static environment as an explicit argument. The arguent list and body must be quoted to prevent them from being interpreted. It's a mess but we'll fix it soon.
(noop (println (Closure '(x y) '(+ (* x x) (* y y)) @nv)) (println ((Closure '(x y) '(+ (* x x) (* y y)) @nv) 3 4)) )
Output

Result
Let
This language doesn't have a 'let' special form, or any other special forms, but we'll make one soon. We do get 'elet' though, which takes an explicit environment and an identifier to assign a value.
(noop (elet @nv 'x 321) (println x) (println (+ x 4000)) )
Output

Result
Hybrid Environments
This is also a mess, and we'll fix it now by writing a simple 'let' function using hybrid environments. When a Closure is appied to arguments, a hybrid environment is made consisting of 3 parts. Now we can write 'let' by using @dnv to get and modify the environment from which the 'let' Closure is called.
(noop "let without explicit nv" (elet @nv 'let (Closure '(key val) '(elet @dnv key val) @nv)) (let 'y 654000) (println (+ y x)) )
Output

Result
Some Fun Stuff
It's a little complicated, but we only have to do it once, and now we can simply use 'let'. Now we can fix that Closure mess, by writing 'lam'. Let's make some other fun stuff while we're at it, including short-circuiting functions, swap, and named functions. Demos below.
(noop "closure without explicit nv" (let 'lam (Closure '(args body) '(Closure args body @dnv) @nv)) "easily reduce value inside a quote" (let 'run (lam '(body) '(@run body @dnv))) "like run but in new scope" (let 'block (lam '(body) '((Closure '() body @dnv)))) "short-circuiting if from _if" (let 'if (lam '(x y z) '(@run (_if x y z) @dnv))) "swap the values of 2 identifiers" (let 'swap (lam '(ida idb) '(noop (let 'tmp (eget @dnv ida)) (elet @dnv ida (eget @dnv idb)) (elet @dnv idb tmp)))) "named closures" (let 'function (lam '(name args body nv) '(last (let 'd (dict)) (let 'C (Closure args body (Env d nv))) (dset d name C) C))) "function without explicit nv" (let 'fun (lam '(name args body) '(elet @dnv name (function name args body @dnv)))) (println "success") )
Output

Result
(noop "runs quote with new hybrid env" (block '(noop (let 'sqr (lam '(x) '(* x x))) "sqr in scope" (println (sqr 6)) )) "sqr out of scope" (println sqr) (println "") "never prints 3" (println (if (println 1) '(println 2) '(println 3))) (println "") "swap" (let 'a 4) (let 'b 5) (println (+ a (+ ", " b))) (swap 'a 'b) (println (+ a (+ ", " b))) (println "") "factorial" (fun 'fact '(x) '(if (== x 0) '1 '(* x (fact (- x 1))))) (println (fact 5)) )
Output

Result
Experiment
Here's a place to experiment. 'last' is a variadic function like 'noop', but it returns its last argument. You may want to
(last "do stuff here" "base environment keys" (println (json (keys (e-d base)))) "user environment keys" (println (json (keys (e-d @nv)))) (date) )
Output

Result
First Attempt
My first attempt at giving applications access to both the static and dynamic environments was to pass both to the interpret function, along with the code to be interpreted. This doesn't work though, because it makes it impossible to get the dynamic parent of the dynamic parent of an environment. For example, if interp/@run took (code, snv, dnv), the short circuiting 'if' would look like:

(let 'if (lam '(x y z) '(@run (_if x y z) @dnv false)))
(if true '(if true '1 '2) '3)

The outer 'if' would want to run the inner 'if' in the dynamic environment from which it was called. However, it wouldn't have access to that environment's dynamic parent, which the inner 'if' needs.
Goals
This achieves the original goals of making a language...
But Why?
Hybrid environments are clearly neither safe, sane, nor fast. All code has access to all code. However, I think there is value in its lack of value. Starting with a fully powerful, small, horrible language gives perspective on what is necessary to make any language safer, saner, and faster. I'm particlarly interested in "safer". Is it possible to sandbox in such a chaotic language? One might have to perform static analysis dynamically. The language might need a full "understanding" of it's own semantics so it could reason about itself, along with the "assertive force" to trust itself.
Made by Alex Varga in 2016
Uses Ace for the editors