Slide 1

Slide 1 text

Chas Emerick @cemerick ClojureWest March 25, 2014 Targeting Clojure & ClojureScript Targeting Clojure & ClojureScript from a Single Codebase from a Single Codebase

Slide 2

Slide 2 text

Writing portable Clojure[Script]: Writing portable Clojure[Script]: Why? Why? ● Validation ● JavaScript environments are useful for general-purpose computation – node.js – Browsers: OpenGL, IndexedDB, WebRTC, typed arrays, web workers, etc… ● JavaScript environments can go where the JVM can't

Slide 3

Slide 3 text

A Brief History of ClojureScript A Brief History of ClojureScript ● First commit 3+ years after Clojure became public ● Implemented assuming protocols – Simpler, easier set of base abstractions ● Portability not an objective, so lots of oh-so-close naming differences – cljs.core vs. clojure.lang – INamed vs. Named – c.l.IPersistentCollection → #{ICollection ICounted IEmptyableCollection}

Slide 4

Slide 4 text

ClojureScript's material differences ClojureScript's material differences ● Hybrid compilation model – Macros written and evaluated in Clojure – :require-macros, :use-macros ● No runtime namespaces ● JavaScript host – “missing” primitives – Different, less capable execution model – All the wat you could want

Slide 5

Slide 5 text

Writing portable Clojure[Script]: Writing portable Clojure[Script]: Options & Problems Options & Problems ● Don't – Maintain separate codebases ● Carefully keep portable and non-portable code separate – Single non-portable call/type/name wags the namespace dog – Lowest common denominator – lein-cljsbuild “crossovers” ● Write code once, translate it for each compilation target

Slide 6

Slide 6 text

Preprocessors in 60 seconds Preprocessors in 60 seconds Preprocessor Source files Compilation environment Env-suitable sources Compiler

Slide 7

Slide 7 text

#ifdef SHARC millis = sharc_dtime(); #else if defined(VxWorks) && defined(X86) millis = read_386_counter(); #else if defined(VxWorks) millis = read_moto_counter(); #else if defined(WIN_32) millis = read_windows_timer(); #endif 30 seconds to go… 30 seconds to go…

Slide 8

Slide 8 text

Feature expressions in LISP Feature expressions in LISP (cons #+Lispm "x" #+Spice "y" #-(or Lispm Spice) "z" x) When `Lispm` is defined: (CONS "x" X) When `Spice` is defined: (CONS "y" X) When neither are defined: (CONS "z" X)

Slide 9

Slide 9 text

cljx: feature expressions for cljx: feature expressions for Clojure[Script] Clojure[Script] ● Operates entirely outside of (before) the reading, macroexpansion, and compilation of Clojure[Script] ● Transformation rules applied to a lossless representation of Clojure[Script] source code (Christophe Grand's sjacket) ● Downstream dependencies only ever see the generated Clojure or ClojureScript code; cljx doesn't leak out of your project ● Available via Leiningen and in the (n)REPL

Slide 10

Slide 10 text

cljx .cljx sources project.clj config, nREPL session type ClojureScript sources ClojureScript Compiler Clojure sources Clojure Compiler

Slide 11

Slide 11 text

cljx example cljx example (ns cemerick.pprng #+cljs (:require math.seedrandom [cljs.core :as lang]) #+clj (:require [clojure.core :as lang]) #+clj (:import java.util.Random) (:refer-clojure :exclude (double float int long boolean)))

Slide 12

Slide 12 text

Transformation targeting Clojure Transformation targeting Clojure (ns cemerick.pprng (:require math.seedrandom [cljs.core :as lang]) (:refer-clojure :exclude (double float int long boolean)))

Slide 13

Slide 13 text

Transformation targeting Transformation targeting ClojureScript ClojureScript (ns cemerick.pprng (:require [clojure.core :as lang]) (:import java.util.Random) (:refer-clojure :exclude (double float int long boolean)))

Slide 14

Slide 14 text

(n)REPL support (n)REPL support ● Problem: want to be able to load/eval cljx code in a REPL session ● Problem': want to be able to load cljx files — if they exist — that correspond to namespaces interactively loaded via ns, require, load-namespace, etc. ● Solution: cljx nREPL middleware hooks clojure.core/load + ClojureScript's – This is safer than it sounds

Slide 15

Slide 15 text

cljx in the wild: notable projects cljx in the wild: notable projects schema formative pprng sablano double-check garden hickory validateur frak cellular enoki inflections-clj pathetic

Slide 16

Slide 16 text

cljx challenges and opportunities cljx challenges and opportunities ● Macros ● Line noise ● Feature expression flexibility: custom transformations ● #lang-style syntax control (viz. Racket)

Slide 17

Slide 17 text

Resources Resources ● cljx: https://github.com/lynaghk/cljx ● “Official” wiki re: feature expressions in Clojure[Script]: http://dev.clojure.org/display/design/Feature+Expressions ● Common Lisp hyperspec, feature expressions: http://www.lispworks.com/documentation/lw50/CLHS/Body/24_aba.htm Thank you! Thank you! Chas Emerick http://cemerick.com http://twitter.com/cemerick http://quilt.org