A language that targets different platforms
Clojure ☛ JVM
ClojureScript ☛ JavaScript
JavaScript Runtimes:
Browsers
Node.js
Rhino
Lisp family
(((((())((((((()))))()()(((())))))))
Functional programming language
A practical language
How is it different from coffeescript, typescript, ...?
Changes some JavaScript semantics (to be saner)
Provides a consistent full language and ecosystem
Values FTW
Immutable by default
Concise and expressive
github.com/joakin/diving-into-clojurescript-exercises
git clone git@github.com:joakin/diving-into-clojurescript-exercises.git
Connect to Lighttable UI
Code samples are in basic/
Prerequisites 01-pre.cljs
Exercise 01.cljs
Solutions are in 01-sol.cljs
Basic syntax
Synonyms
himera.herokuapp.com/synonym.htmlclojure.core
http://clojure.github.io/clojure/clojure.core-api.html
http://clojuredocs.org/clojure_core/1.3.0/clojure.core
git clone git@github.com:swannodette/lt-cljs-tutorial.git
Do it yourself
Check the docs
Look at the community results (Follow top members)
We are going to walk through the creation of a canvas fullscreen app
A wall of colorful squares that vibrate
joakin/diving-into-clojurescript-exercises/futurejs-wall
Steps are stored in src-steps (see README)
Interesting things to learn
We are going to make a web app that will consume a service
via ajax and will render the contents.
joakin/diving-into-clojurescript-exercises/lt-plugins
Steps are in the README
lein help tutorial | less
lein help new
lein new mies awebsite
How do I compile ClojureScript?
Leiningen plugin for compiling ClojureScript
In project.clj ☞
:plugins [[lein-cljsbuild "1.0.2"]]
Sample conf:
:cljsbuild {
:builds [{:id "awebsite"
:source-paths ["src"]
:compiler {
:output-to "awebsite.js"
:output-dir "out"
:optimizations :none
:source-map true}}]})
lein cljsbuild once awebsite # just build once
lein cljsbuild auto awebsite # watches changes and recompiles incrementally
Clojurescript compiler features several compilation modes
project.clj with all the options commented
Each mode has different tradeoffs:
Clojurescript compiler ☞ cljs ► standard library & js
Google closure compiler ☞ merging files, minification, optimizations
:cljsbuild {
:builds [{:id "awebsite"
:source-paths ["src"]
:compiler {
:output-to "awebsite.js"
:output-dir "out"
:optimizations :none
:pretty-print true
:source-map true}}]}
<script src="out/goog/base.js" type="text/javascript"></script>
<script src="awebsite.js" type="text/javascript"></script>
<script type="text/javascript">goog.require("awebsite.core");</script>
:cljsbuild {
:builds [{:id "awebsite"
:source-paths ["src"]
:compiler {
:output-to "awebsite.js"
:output-dir "out"
:optimizations :whitespace
:pretty-print true
:source-map true}}]}
<script src="awebsite.js" type="text/javascript"></script>
:cljsbuild {
:builds [{:id "awebsite"
:source-paths ["src"]
:compiler {
:output-to "awebsite.js" ; No output-dir (only the file)
:optimizations :simple
:pretty-print true
:source-map "awebsite.js.map"}}]} ; Sourcemaps are a file now, not true
<script src="awebsite.js" type="text/javascript"></script>
:cljsbuild {
:builds [{:id "awebsite"
:source-paths ["src"]
:compiler {
:output-to "awebsite.js"
:optimizations :advanced}}]}
<script src="awebsite.js" type="text/javascript"></script>
Options pretty-print and source-maps are optional.
They increase compilation time.
(Incrementally not so much)
Use :optimizations :none with :pretty-print true and :source-maps true
for development workflows (using lein cljsbuild auto (pure delight))
Use :optimizations :advanced for staging and production.
(Staging with :source-maps and :pretty-print, production without).
lein cljsbuild once
Successfully compiled "awebsite.js" in 9.214 seconds.
Successfully compiled "awebsite-w.js" in 14.045 seconds.
Successfully compiled "awebsite-s.js" in 19.269 seconds.
Successfully compiled "awebsite-p.js" in 18.701 seconds.
lein cljsbuild auto
Successfully compiled "awebsite.js" in 0.05 seconds.
Successfully compiled "awebsite-w.js" in 4.126 seconds.
Successfully compiled "awebsite-s.js" in 5.918 seconds.
Successfully compiled "awebsite-p.js" in 4.188 seconds.
We can distinguish 3 kinds of libraries that we can use
(and require different treatment)
Hosted in lein compatible repos (clojars)
How to find them:
Or Google
Just include them in your project.clj and go:
:dependencies [[org.clojure/clojure "1.5.1"]
[org.clojure/clojurescript "0.0-2173"]
[rm-hull/monet "0.1.11"]]
Restart the compiler so that it downloads it
Big set of cross browser libraries written by Google
Example:
API, importing & real_world_usage
Lots of interesting stuff: Format, Color, Events, Dom, Style, ...
Takes advantage of Advanced compilation
(deletes code you don't use and inlines code where it can)
The compiler has and includes as necessary a version of the closure libs
You can specify a specific version of the google closure libraries:
:dependencies [[org.clojure/clojure "1.5.1"]
[com.google.javascript/closure-compiler "v20131014"]
[org.clojure/clojurescript "0.0-2173"
:exclusions [com.google.javascript/closure-compiler]]
Libraries written with the closure compiler optimizations in mind.
Not a whole lot of them.
They take advantage of code optimizations and compiler errors (docs)
Example (Download react to your project):
:compiler {
; ... rest of arguments
:foreign-libs [{:file "reactjs/react.js"
:provides ["React"]}]
:externs ["reactjs/externs/react.js"]}
Almost all of the JS libs.
Include the code in your project,
If it has externs, you include them:
:preamble ["react/react_with_addons.min.js""gifjs/dist/gif.js"] ; or link them in the html:externs ["react/react_with_addons.js""gifjs/dist/gif.js"]
Pros:
HUGE ecosystem
Lots of good libraries
(moment.js, react.js, hammer.js, bluebird, async, d3.js, bacon.js, ...)
Cons:
No optimizations or unused code removal
Bigger size of bundle because of libraries
We have to take care of externs with advanced
Suggestion:
If there is a good cljs lib, use that. Otherwise, go wild.
Secretary | Client side router |
Processing.cljs | Wrapper over processing.js |
Dommy Enfocus Domina | Dom manipulation and templating library |
Om Reagent | Interface to React.js (web apps) |
Javelin | Spreadsheet-like dataflow programming |
Monet | Easy and performant canvas and visuals |
Jayq | Idiomatic cljs - jQuery wrapper |
cljs-ajax cljs-http | HTTP and AJAX library |
Lucuma | Web components |
Servant | Web workers |
cljs-time | Date and time library |
Inkspot | Colors and swatches |
...
Live development is no substitute for tests
Several options:
clojurescript.test
Full complete test library (port of the clojure version)
Well mantained and documented
To use it just add it to your plugins in project.clj
:plugins [[com.cemerick/clojurescript.test "0.3.0"]]
:cljsbuild {:builds [ ... ]
:test-commands {"unit-tests" ["phantomjs" :runner
"this.literal_js_was_evaluated=true"
"target/cljs/testable.js"
"test/cemerick/cljs/test/extra_test_command_file.js"]}}
:test-commands {"unit-tests" ["node" :node-runner
; extra code/files here...
]}
Simple and easy to understand
(deftest somewhat-less-wat
(is (= true (some-function "whatever"))))
Both sync and async support
(deftest ^:async timeout
(let [now #(.getTime (js/Date.))
t (now)]
(js/setTimeout
(fn []
(is (>= (now) (+ t 2000)))
(done))
2000)))
BDD style testing (specs)
:dependencies [[org.clojure/clojure "1.5.1"]]
:profiles {:dev {:dependencies [[speclj "3.0.0"]]}}
:plugins [[speclj "3.0.0"]]
:test-paths ["spec"]
:cljsbuild {:builds {:dev {:source-paths ["src/cljs" "spec/cljs"]
:compiler {:output-to "path/to/compiled.js"}
:notify-command ["bin/speclj" "path/to/compiled.js"]}
:test-commands {"test" ["bin/speclj" "path/to/compiled.js"]}}
(ns sample.core-spec
(:require-macros [speclj.core :refer [describe it should should-not run-specs])
(:require [speclj.core]
[sample.core :as my-core]))
(describe "Truth"
(it "is true"
(should true))
(it "is not false"
(should-not false)))
(run-specs)
Jasmine based testing (used with Karma)
:dependencies [[im.chit/purnam "0.4.3"]]
(describe "Addition"
(it "should add things"
(is (+ 1 1) 2)))
There are more, and also wrappers for JS libraries.
You could ignore all this and just use a JS library for testing (interop)
(Why?)
Instead of enumerating
expected input and output for unit tests,
you write properties about your
function that should hold true for all inputs.
(def sort-idempotent-prop
(prop/for-all [v (gen/vector gen/int)]
(= (sort v) (sort (sort v)))))
(tc/quick-check 100 sort-idempotent-prop)
(defspec first-element-is-min-after-sorting ;; the name of the test
100 ;; the number of iterations for test.check to test
(prop/for-all [v (such-that not-empty (gen/vector gen/int))]
(= (apply min v)
(first (sorted v)))))
Code generating/rewritting code that gets run at compile time.
(Code is data, as you have seen)
ClojureScript macros are written in clojure (.clj files)
Reading, evaluation and macros
Super powerful, useful, language is infinitely extensible by users
Complicated to debug, best avoided unless necessary (maybe)
(defmacro when
"Evaluates test. If logical true, evaluates body in an implicit do."
{:added "1.0"}
[test & body]
(list 'if test (cons 'do body)))
(macroexpand '(when (the-cows-come :home)
(call me :pappy)
(slap me :silly)))
; =>
(if (the-cows-come :home)
(do (call me :pappy)
(slap me :silly)))
(defmacro unless
"Inverted 'if'"
[test & branches]
(conj (reverse branches) test 'if))
(macroexpand '(unless (done-been slapped? me)
(slap me :silly)
(say "I reckon that'll learn me")))
; =>
(if (done-been slapped? me)
(say "I reckon that'll learn me")
(slap me :silly))
They are a tough topic, and great books and articles are available.
Look for Clojure macros, not ClojureScript specifically
Super powerful, enable users to extend the language via libraries
in ways inconceivable in other languages (JS? Damn ECMA)
We will see the power in the next section.
Some uses:
Rewrite code (unless, core.async)
New syntax constructs
Optimize code (compile time) (dommy)
Mozilla leaded effort to have macros in JavaScript
Opt-in macros
More verbose because syntax (code is not data),
but brings the power and responsibility to JS
Example: ES6 syntax, Ki (lisp+mori)
Great philosophy, vision, community and people
with a great language produce awesome powerful things
Go style concurrency (CSP) implemented as a library!
Turns syncronous looking code into CPS (callbacks)
Concurrent code in single threaded JS!
(defn init []
(let [clicks (listen (dom/getElement "search") "click")
results-view (dom/getElement "results")]
(go (while true
(<! clicks)
(let [[_ results] (<! (jsonp (query-url (user-query))))]
(set! (.-innerHTML results-view) (render-query results)))))))
(defn get-user [id]
(let [out (chan)]
(db-query-user {:id id} (fn [user] (put! out user)))))
(defn update-user [user]
(db-update-user user (fn [err] (put! out err))))
(def handler [req res]
(go
(let [user (<! get-user (.id req))
result (<! update-user user)]
(.send res (if (nil? result) "OK" "BROKE"))))
(let [c1 (chan)
c2 (chan)]
(go (while true
(let [[v ch] (alts! [c1 c2])]
(println "Read" v "from" ch))))
(go (>! c1 "hi"))
(go (>! c2 "there")))
(go (try
(let [tweets (<? (get-tweets-for "swannodette"))
first-url (<? (expand-url (first (parse-urls tweets))))
response (<? (http-get first-url))]
(. js/console (log "Most recent link text:" response)))
(catch js/Error e
(. js/console (error "Error with the twitterverse:" e)))))
An optimized pattern match and predicate dispatch library for Clojure.
(doseq [n (range 1 101)]
(println
(match [(mod n 3) (mod n 5)]
[0 0] "FizzBuzz"
[0 _] "Fizz"
[_ 0] "Buzz"
:else n)))
(let [x 1 y 2]
(match [x y]
[1 b] b
[a 2] a
:else nil))
(let [x {:a 1 :b 1}]
(match [x]
[{:a _ :b 2}] :a0
[{:a 1 :b 1}] :a1
[{:c 3 :d _ :e 4}] :a2
:else nil))
(let [x [1 2 3]]
(match [x]
[[_ _ 2]] :a0
[[1 1 3]] :a1
[[1 2 3]] :a2
:else :a3))
Prolog-like relational programming, constraint logic programming,
and nominal logic programming for Clojure
(run* [q]
(== q true))
;;=> (true)
(run* [q]
(membero q [1 2 3])
(membero q [2 3 4]))
;;=> (2 3)
(run* [q]
(== {:a q :b 2} {:a 1 :b 2}))
;;=> (1)
(run* [q]
(fresh [a]
(membero q [1 2 3])
(membero a [3 4 5])
(== q a)))
;;=> (3)
Gradual typing in Clojure, as a library.
(ann ^:no-check foo (Fn [Number -> Number]))
(defn foo [a]
'a)
(ann bar [Number -> Number])
(defn bar [b]
(+ 2 (foo b)))
Learn new things that suppose significant brain effort
Bring what you learned to your daily work (at least the concepts)
Embrace immutability (eases reasoning about code)
Play and have fun
Joaquin