Skip to content

Bootstrapped ClojureScript FAQ

Mike Fikes edited this page Apr 8, 2018 · 41 revisions

Does bootstrapped ClojureScript mean that I'll be able to develop ClojureScript without the JVM?

It is not a goal of bootstrapped ClojureScript to eliminate the JVM.

  • The JVM is used by Google Closure Compiler. Thus a JVM needs to be present for anything other than compiler :optimizations set to :none. While :none is common for REPL development, :advanced is often used for production builds. While it is possible to run Closure Complier as a separate step, ClojureScript now has non-trivial in-process integration which provides enhanced capability for essential features like source mapping and code splitting.
  • The JVM implementation is efficient. As things stand now it usually leads to faster reading, analysis, and compilation than most JavaScript engines. With bleeding edge builds, compared to the JVM when compiling all of cljs.core (the standard library), JavaScriptCore is 1.5X slower, V8 is 2.75X slower, and SpiderMonkey is 3.3X slower.
  • Much of the existing popular and mature ClojureScript tooling (Leiningen, Boot, Figwheel) have fairly deep assumptions about the conventions of the JVM classpath as well as JVM-centric dependency resolution (Maven).

While there may be some advantages to a completely JVM-free implementation, developing and maintaining such a thing is not free, and is thus not a goal of the project.

Having said that, there is nothing preventing the community creation of development tools (build tools, REPLs, IDEs, etc.) that make use of bootstrapped ClojureScript without a JVM present. However, while such alternative community developments are encouraged, it is clear that it will take considerable time and effort to achieve parity with the existing JVM-aware tooling already actively maintained by the community.

The Lumo self-hosted environment provides such a capability. In particular see Compiling ClojureScript Projects Without the JVM.

Why was bootstrapped ClojureScript created then?

Primarily to broaden the scope of reachable targets. The goal is not to eliminate the JVM at development time but rather reach targets who cannot realistically provide the JVM runtime.

Without bootstrapped ClojureScript, if you want to build an application that can evaluate ClojureScript forms at runtime, you need to delegate that task to some backend compiler service (this is how Himera works). By enabling the compiler to be used from within the target JavaScript environment, novel use cases can be implemented. For example:

  • Shipping dev tools that don't require a JVM dependency.
  • Low-latency scripting, making use of JavaScript engines' inherent fast startup in interpreted mode. Currently bootstrapped ClojureScript REPLs are 20-30% faster to start up than Clojure and tens of times faster than the standard JVM based ClojureScript REPLs.
  • Live updates to deployed codebases. (Ship code that can be compiled in target environment and dynamically incorporated in app. Mobile app updates, etc.)
  • Web pages that provide dynamic online tutorials or showcase ClojureScript libraries.
  • REPLs on mobile devices, without requiring network access to a compiler backend.
  • Diagnostic REPLs embedded in apps, making it easier to examine state of running app.

The community is just beginning to scratch the surface on what is possible with this new capability!

With bootstrapped ClojureScript, can we now have namespaces that mix both macros and functions?

An overarching design goal of bootstrapped ClojureScript is that namespaces be uniformly compilable, regardless of whether they are being compiled by the JVM-based implementation or when compiled in bootstrapped mode. Thus, bootstrapped ClojureScript intentionally preserves the constraints that have always been present.

With bootstrapped ClojureScript, can we have cljs.core/eval?

A new cljs.js namespace is being introduced which exposes additional runtime capabilities for those applications that wish to opt in.

The ClojureScript 1.10.238 release introduces a cljs.core/eval which essentially behaves like clojure.core/eval when in self-hosted environments, but throws otherwise.

Clone this wiki locally