If you construct a string like so
(def s (pr-str {:greet '(partial str "Hello" " " "World!")}))
How do you read the structure using a reader (i.e. read-string
) and pull the value for the :greet
key back as a function than can be invoked?
Note that by quoting the code it preserves the shape. If I drop the quote it serializes the guts of the underlying javascript function. I tried a backtick (`) too.
The goal is to be able to save off functions that a user constructed in some app, serialize those to edn and then later deserialize that text, pull out functions that are capable of being invoked.
The above bit of code should return "Hello World!" when invoked.
Safety can be addressed separately.
You could make it work by using the same solution http://clojurescript.net/ is using - which is cljs-bootstrap basically.
I prepared a demo repository you can clone and run. It contains a simple web page with an input you can type into, which will be live evaluated. Code is very short and simple, so it should be easy to follow and adapt to your needs. It looks like this:
Step 1: Get cljs-bootstrap file compiled to JS. It contains the read_eval_print
method we'll use. Load this file in your HTML file before you load your compiled CLJS files.
Step 2: Since we use a JS, and not a proper CLJS dependency, we might need externs (i.e. in advanced compilation mode):
var cljs_bootstrap = {};
cljs_bootstrap.core = {};
cljs_bootstrap.core.read_eval_print = function() {};
Remember to add them to your project.clj
.
Step 3: read_eval_print
accepts two arguments - first is a string with the ClojureScript code, second is the callback that will be called once evaluation completes. This code will do:
(let [code "(prn ((partial str \"Hello\" \"World\") \" :)\"))"
cljs (-> js/window .-cljs_bootstrap .-core)]
(.read_eval_print
cljs
code
(fn [success _] (prn "Success?" success))))
It's pretty straightforward actually, as you can see:
cljs
is used to obtain the read_eval_print
method from the window object. That's because we import this file as a plain JS dependency, not a CLJS one (that's also the reason we don't need to (:require)
anything.This code, when executed in the browser, prints that to the JS console:
That's pretty much it.
(:require)
it. I didn't have time to play with it though.