ClojureScript master now has cljs.core/eval
. This delegates to cljs.core/*eval*
which, by default throws, but you can bind it to any implementation that can compile and evaluate ClojureScript forms.
If you require the cljs.js
namespace (which is the main support namespace for self-hosted ClojureScript), then cljs.core/*eval*
is set to an implementation that uses self-hosted ClojureScript for this capability. This means that all self-hosted ClojureScript environments will now have a first-class eval
implementation that just works. For example, Planck master:
$ planck -q
cljs.user=> (eval '(+ 2 3))
5
But, this doesn't magically cause eval
to start working for regular JVM-based ClojureScript environments.
Having said this, you can play around with this in regular JVM-based ClojureScript if you evaluate the forms in setup.cljs
below. All this does is appropriate the self-hosted implementation of eval
and make it (somewhat) useable from JVM ClojureScript.
You can launch a browser REPL using the clj
command here that sets things up to use ClojureScript master and the code in setup.cljs
:
clj -Sdeps '{:deps {github-mfikes/cljs-eval {:git/url "https://gist.github.com/mfikes/66a120e18b75b6f4a3ecd0db8a976d84" :sha "542208abcc4b77c90300d70e9ebd7a89d88fe460"}}}' -m cljs.main -e "(require 'cljs.js)" -i @setup.cljs -r
Note: The above employs an extra
-e
to work around CLJS-2663.
With this, try eval
directly in your JVM-based REPL:
cljs.user=> (eval '(+ 1 2))
3
cljs.user=> (eval (list inc 10))
11
That last example is neat in that it compiling to code that embeds a reference to the inc
function value in the emitted JavaScript. For more details on this twist see ClojureScript eval, which describes the basis for the technique used in ClojureScript master.
Does this mean you can use eval
in your regular ClojureScript code now? Not really, but perhaps you can in limited use cases. The reason is that normally ClojureScript is compiled down to JavaScript that executes without a compiler environment, and in particular lacks the state needed to support the things that eval
really needs. In fact, the "trick" used here is limited in that it let
s its own compiler state—via (cljs.js/empty-state)
—that lives only on the JavaScript side of things (in your browser), and is independent of your JVM ClojureScript compiler state. Even though both share the same JavaScript evaluation enviornment, you can see the effect of the two separate compiler states via example:
cljs.user=> (def a 3)
#'cljs.user/a
cljs.user=> a
3
cljs.user=> (eval 'a)
WARNING: Use of undeclared Var cljs.user/a
3
cljs.user=> (eval '(def b 5))
#'cljs.user/b
cljs.user=> (eval 'b)
5
cljs.user=> b
WARNING: Use of undeclared Var cljs.user/b at line 1 <cljs repl>
5