ClojureScript Day #1 Important things to note This should be a dialog, not a lecture stop me, ask questions, understand deeply There are still many things I don’t know or haven’t decided some tasks will be research some pushback welcome I reserve BDFL rights :) This is an opportunity to get involved early stay flexible to avoid pain Welcome Chouser! One of my first and best users and contributors Someone whose opinions I value, and a Clojure expert Author of a Clojure book not (yet) working at Relevance! The first to walk down the ClojureScript road This is a key Clojure/core (with help) deliverable We do more than maintain, we lead community should be stunned (shhh!) I’m very excited about this aspect let’s knock this out of the park! Intro and rationale Problem statement Javascript is the only programmable technology in key target environments i.e. the browser nothing will change that for years to come Javascript has the greatest reach in other key environments i.e. mobile Javascript (the language) is not very robust Fewer good parts than bad parts Much convention and discipline required to avoid headaches Conventions differ between shops, libs Ever increasing pressure to create richer applications in these environments requiring more and larger libraries ordinary minification doesn’t scale up increasing requirements complexity can’t add language or environment complexity on top Rationale solving this problem will give developers important leverage empower them to tackle more difficult problems with greater confidence in the robustness of their solutions inspire next-generation approaches to web and mobile development Strategy Compile (a subset of) Clojure to Javascript reach everywhere JS does Clojure is simpler, more robust, more concise, more powerful overall than JS yet maps well to JS as implementation artifact Leverage best-of-breed JS appraoches Currently, IMO that is Google’s, with Closure compiler and libraries called gclosure hereafter Fortunately both open sourced Gclosure’s strategy is whole-program optimization resulting application includes only code actually used this is essential to writing large (and small) applications against large and manifold libraries while allowing those libs to be written in a straightforward, non-clever manner This is not just about the browser Node.js, plugins anywhere JS is accepted, any future JS-based environments Non-objectives complete Closure subset, but try to make identical features identical document differences porting large applications in their entirety portability layers unifying JS and Java cross platform reach is about moving core competencies and libraries, not everything Profit! ClojureScript becomes the most powerful language for generating the smallest and fastest JS applications ClojureScript runs everywhere JS runs This is Clojure’s client story This is Clojure’s mobile story A powerful tool for anyone willing to learn Clojure Tactics Don’t change Clojure itself even though it might make things easier track those things we’d like to be different, and work into Clojure dev schedule The ClojureScript compiler is written in Clojure The reader is Clojure’s Macros are written in Clojure therefor, no compiler at runtime, no eval browser-hosted REPL a non-target! also, some things that are runtime-reified in Clojure (namespaces, Vars) may not be in ClojureScript GClosure’s strategy requires JS to be written in a particular idiom especially for the most advanced optimization ClojureScript will always generate code compliant with advanced optimizations ClojureScript will use the same packaging and dependency strategy as gclosure The gclosure library is an accepted dependency but nothing else (other than base JS) ok, and maybe some stealing from GWT output, if we’re desperate but that’s really it in particular, use gclosure for all environmental conditionality we make no per-browser decisions ourselves The gclosure compiler is optional, but recommended for final delivery but don’t be too stupid without it The compiler has an enriched primitive set (vs Clojure’s) deftype defprotocol extend-type need no runtime lib allows bootstrap abstraction and data structures to be written in terms of these The runtime library is completely written in ClojureScript No Javascript! js* primitive escape hatch to reach gnarly stuff that ClojureScript won’t expose Presumptions JS is single-threaded, forever nevertheless, we will use Clojure reference primitives (at least atom) Roadmap Compiler It’s alive! a few more primitives to go output needs to be sussed out esp tested with gclosure compiler some niceties missing (load-file etc) Libraries Many core macros imported and work testing required some missing, like binding, dotimes This space intentionally left blank (core.cljs) that’s why you’re here! Much work, but following trodden ground Move the core abstractions to protocols Implement the core data structures with deftype copy fn impls, tweaking as needed Tooling ClojureScript written to the spec of gclosure Actual integration with tool chain TODO Deps builder Compilation with gclosure compiler drive through API for greatest control vs CLI finding/loading gclosure lib testing delivery REPL and other expected dev conveniences load (file), load-js Inside the compiler I will not be the only one who knows this! only 500 lines Recursive descent parser 2 phases analyze code data in -> AST data out all ordinary Clojure data each expr recursively analyzes nested exprs checks for correct structure emit AST data in -> print JS (via side effect to out) this allows with-out-str, or direct to file alternative - thread output stream, meh each expr recursively emits nested exprs Both analyze and emit are polymorphic using multimethods other than a little hand-routing at top of analyze, no global switch statement extend the compiler by defining parse and emit methods add special ops to specials set The threaded environment (env) most important, :context and :locals all name-introducing exprs (e.g. fn, let) augment the environment before passing to children Tricky bit - :context In ClojureScript everything is an expression but not in JS optimal output requires knowledge of the context :statement, :return, :expr non-exprs in expr contexts require transformation (usually a function wrapper) Primitives walkthrough if def fn* do let* loop* recur new set! ns deftype* . js* Macros walkthrough macroexpansion defprototype extend-type import-macros the core/core trick Evaluation model top down, form at a time just like Clojure just like JS What’s missing? validation that compilation ns resolved names exist in ns more correctness checking better error messages … strategy for apply What’s where src/main/clojure/cljs/compiler.clj the compiler src/main/clojure/cljs/core.clj core macros src/main/cljs/core.cljs core library Todo separate org file Breakout and tackle tasks we’ll substantially be in the same file ideas for making that work? Regroup and feedback