Lisp existed already before Clojure and has a rich history, much of which is not in Clojure.
ClojureScript is interpreter, unless you run Closure compiler in compilation phase. Interpretation is done by default in repl. There is also Joker [0].
> no linked lists as base data structure
Not true. Linked lists are base data structure on the same level as vectors and hash maps. Unlike Scheme where hash maps are sometimes implemented as assoc-ed lists.
> no Lisp in Lisp
Check for CinC and derivatives [1] or Ferret [2].
> no Lisp runtime
What you define by "runtime"?
> no images
By this requirement, 90% Lisps out there would not be Lisp ;) Even some Common Lisp implementations (ABCL) doesn't have it. Or if you want another extreme, V8/nodejs can generate image/snapshot or you can dump JVM image via Criu [3].
> not the typical debugging tools like break loops
IntelliJ has it, CIDER has it, there is clj-debugger... It is not part of default implementation, just like Scheme doesn't have it.
I thought ClojureScript compiles to JavaScript?
> What you define by "runtime"?
For example the stuff the JVM provides: memory management, data layout, interrupt handling, threads, loading code, talking to the environment, ...
> By this requirement, 90% Lisps out there would not be Lisp
I'd think it's more like 70% use images... just a guess. The exceptions usually are Lisps using runtimes which can dump/load images (like probably every Lisp on the JVM) or which by choice don't do (like some Lisp to C compilers, ...).
> Even some Common Lisp implementations (ABCL) doesn't have it.
That's the same limitation of the JVM. There are a few more implementations which don't have it. But generally there is a rich choice of full language implementations with and without images.
According to [0], these days it is hard to distinguish between compilers/interpreters. However, it translates code to javascript and execute it immediately, just like python does (converting code to own bytecode, before execution). No translation to machine language.
> For example the stuff the JVM provides...
I still fail to see what (Common) Lisp runtime gives over JVM, except direct translation to machine language (Hotspot does that in runtime) and maybe restarts - there are few clojure libraries which implements this, but thing would be much better if is baked in language/jvm.
> I'd think it's more like 70% use images
If you count into that Scheme implementations, that number would be significantly different ;)
A Lisp interpreter runs directly the s-expressions. This is independent from the idea of evaluation. Evaluation could be implemented by an interpreter or by a compiler.
For example using an interpreter:
CL-USER 32 > (defun fak (n)
(if (zerop n)
1
(* n (fak (1- n)))))
FAK
Let's break into the function if the argument N is 0: CL-USER 33 > (trace (fak :break (eql (first *traced-arglist*) 0)))
(FAK)
CL-USER 34 > (fak 5)
0 FAK > ...
>> N : 5
1 FAK > ...
>> N : 4
2 FAK > ...
>> N : 3
3 FAK > ...
>> N : 2
4 FAK > ...
>> N : 1
5 FAK > ...
>> N : 0
Break on entry to FAK with *TRACED-ARGLIST* (0).
1 (continue) Return from break.
2 Continue with trace removed.
3 Continue traced with break removed.
4 Continue and break when this function returns.
5 (abort) Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
CL-USER 35 : 1 > :bq
FAK <- FAK <- FAK <- FAK <- FAK <- EVAL <- CAPI::CAPI-TOP-LEVEL-FUNCTION <- CAPI::INTERACTIVE-PANE-TOP-LOOP
<- MP::PROCESS-SG-FUNCTION
CL-USER 36 : 1 > :n
Interpreted call to FAK
CL-USER 37 : 1 > :lambda
(LAMBDA (N) (DECLARE (SYSTEM::SOURCE-LEVEL #<EQ Hash Table{0} 41B01C37FB>))
(DECLARE (LAMBDA-NAME FAK))
(IF (ZEROP N) 1 (* N (FAK #))))
You see above the actual s-expression being executed for that stack frame. It's not executing any compiled or translated code. It's actually interpreting the source directly without any such step.If we want, we could inspect, alter it with the usual Lisp functions and continue running.
CL-USER 39 : 1 > (fifth *)
(IF (ZEROP N) 1 (* N (FAK (1- N))))
CL-USER 40 : 1 > (setf (third *) 2)
2
CL-USER 41 : 1 > **
(IF (ZEROP N) 2 (* N (FAK (1- N))))
CL-USER 42 : 1 > :c 1
5 FAK < ...
<< VALUE-0 : 2
4 FAK < ...
<< VALUE-0 : 2
3 FAK < ...
<< VALUE-0 : 4
2 FAK < ...
<< VALUE-0 : 12
1 FAK < ...
<< VALUE-0 : 48
0 FAK < ...
<< VALUE-0 : 240
240
> I still fail to see what (Common) Lisp runtime gives over JVMMore efficient data representation, more efficient execution, saving and loading of images, tailored GCs, mixing compiled and interpreted (see above) Lisp code, deeper runtime inspection, resumeable exceptions, efficient stack traces, efficient function calls, fast startup times, etc etc.
Something like Lisp, which may have a bunch of very dynamic parts, is hard to implement efficiently on the JVM. Clojure is designed to map better to the JVM.