Exploring ClojureScript
Introduction to building apps with
ClojureScript
Luke Donnet (Github @BadAlgorithm)
(In 2019, if you are not doing ClojureScript you are doing JavaScript wrong.)
1. Introduction
1.2 – About me
• Software Developer for Adzurra
• Current day job stack
§ React/Redux
§ Angular 5/Ionic 3
§ Clojure
§ AWS
• Have been doing Clojure for ~1 year
1.3 – “Why should I care?”
• Something new?
• Has made my life easier
• Functional Programming
1.3 – Clojure (.clj)
• Part of the LISP (List Processor) Family
• Processes code as data
• Fully functional programming
• Clojure runs on the JVM
• Rich Hickey is the Benevolent dictator for life (BDFL)
1.4 – Syntax is Easy
(+ 1 2) ; equals 3
That’s it!
Operator
Arguments
1.5 – ClojureScript (.cljs)
• ClojureScript compiles Clojure to JS
• Can pretty much run wherever JS can
§ Web
§ Nodejs
http://clojurescript.org
2. Language Features
2.1 – Basics: Hello world
(ns hello-world.core)
(def hello-message "Hello world")
(defn print-message
[message]
(print message))
(print-message hello-message)
; Prints "Hello world" - returns nil
2.2 – Basics: Returning from functions
(ns hello-world.core)
(def hello-message "Hello world")
(defn print-message
[message]
(print message)
"Return me")
(print-message hello-message)
; Prints "Hello world" – returns ”Return me"
2.3 – Types
(def a-number 1)
(def also-a-number 4.321)
(def a-string "I'm a String")
(def a-keyword :foo-bar)
(def a-namespaced-keyword ::foo-bar) ; turns into :explore-types.core/foo-bar
(def a-bool? true)
(ns explore-types.core)
2.4 – Data Structures
(def a-map {:key-foo "foo" :key-bar :bar :baz-key a-bool?})
(def a-list '(1 2 3 4 "Hello" :foo a-string))
(def a-vector [:hello "bye" {:foo-bar a-number}])
(def a-set #{1 2 3 4 5 6})
2.5 – Anonymous Functions
(def a-anon-fn (fn [] "I'm a function"))
(def shorthand-function #(print %))
2.6 – Example comparison
const result = "foo" === "bar" ? "yea" : "nah";
(def result (if (= "foo" "bar") "yea" "nah"))
Ternary operator
Destructuring
const {foo, bar} = {foo: "foo", bar: "bar", baz: "baz"}
(let [{:keys [foo bar]} {:foo "foo" :bar "bar" :baz "baz"}])
2.7 – Simple in Cljs… no so much in JS
Remove multiple keys from object
A sum of numbers in a vector
Generate a range of values
(def my-numbers [1 2 3 4 5 6 7 8 9 10])
(apply + my-numbers)
(def my-map {:one "one" :two "two" :three "three" :four "four"})
(dissoc my-map :one :two)
; Or if the list of items to remove is in a vector
(def remove-these [:one :two])
(apply (partial dissoc my-map) remove-these)
(range 11)
2.8 – Useful functions
The ones we know and love
(for [number my-numbers] (println number)) ; Print each value
(map inc my-numbers) ; Increment all numbers
(filter odd? my-numbers) ; Returns only odd numbers
(reduce #(conj (vec %)) [] my-numbers) ; Returns [[1] [2] [3] ...]
2.9 – Useful functions (cont)
The ones we don’t know we love yet…
(walk) ; Traverse a datastructure
(mapcat) ; concat all items in a vector
(interleave) ; Return new collection in alternating order
(interpose) ; Insert something in between every item
(take-while) ; Take each item while predicate is true
(drop-while) ; Remove each item while predicate is true
(take-last) ; Get last item
(next) ; Get everything expect first item
And many more…
2.10 – Many useful functions come built-in
• Works out-of-the-box
• Limits the number of deps
(think of left-pad)
• No more scouring stack-
overflow for trivial tasks
2.12 – Macros (might never be in ES*)
• Being a Lisp we can use Macros
• Extends the language
• Clojure which writes clojure…
• Use only when needed
(defmacro infix
"Addition for mere mortals"
[infixed]
(list
(second infixed)
(first infixed)
(last infixed)))
(infix (1 + 1))
2.13 – Built-in macro example
Threading macro, passes the output of one function into
the input of another
(->> my-numbers
(map inc)
(filter odd?)
(reduce #(conj (vec %)) []))
Chain of responsibility pattern made simple
(reduce #(conj (vec %)) [] (filter odd? (map inc my-numbers)))This
Becomes this
2.14 – Atoms
• Immutability?
• Nothing will change if nothing
changes
• Atoms gives us shared state
• Changes are synchronous
(let [my-val 0]
(println my-val) ; 0
(inc my-val)
(println my-val)) ; 0
(let [my-val (atom 0)]
(println @my-val) ; 0
(swap! my-val inc)
(println @my-val)) ; 1
Nothing changes
Value changes
2.15 – Unit Testing
• A lot simpler since functions are designed to be pure
• No need to integrate with a zillion libraries
(deftest validate-form
(testing "Should return true if the fields are complete and no fields are empty"
(let [mock-signup {:company "my company"
:email "email@account.com"
:password "thisisapassword"}]
(is (not (contains? (validate-signup mock-signup) :response)))
(is (every? (validate-signup mock-signup) [:company :email :password])))))
2.16 – Mocking with-redef
• Mocking is easy
• Eliminate the need for
complex integration
with spies, stubs,
mocks…
• Out-of-the-box
(defn call-me-maybe?
[]
"Hey, I just met you")
(defn test-me
[]
(call-me-maybe?))
(deftest called?
(testing "Check if function is called"
(let [count (atom 0)]
(with-redefs [call-me-maybe? #(swap! count inc)]
(test-me)
(is (= @count 1))))))
2.17 – Further reading
We’ve only just scratched the surface of Clojure/ClojureScript
• Core Async
• Cljs and JS interop
• Structural sharing (answers: how can immutability be efficient?)
• Advanced compilation
• Lazy sequences
• Transducers (very powerful tool) / Transients
• GoF patterns in Clojure http://mishadoff.com/blog/clojure-design-patterns/
3. Production Ready?
3.1 – Community/Support
• Small but active
• Many leading star open source projects
and devs
• Docs are great
• Heaps of support through slack/github
and others
3.2 – Cljs in the wild - CircleCI
• Great CI/CD system
• Use Clojure and ClojureScript full stack
3.3 – Cljs in the wild - JESI
• Journey Management app
• Built with reframe and
reagent
• Manage to operate with a
small team (lean and
mean)
3.4 – Cljs in the wild – Roudolph
• Christmas light finding app
• Completely serverless backend
built with Cljs
• Mobile app being rebuilt as a
PWA with cljs
4.Tooling
4.1 – Leiningen
• Build automation and
dependency management
• Easy to use cli, spin up projects
in seconds
https://leiningen.org/
4.2 – Figwheel and Shadow Cljs
• Build system for cljs projects
• Super fast quick reloading
• No need to re-enter form data across
reloads
• Shadow-cljs excels at ease of use and
support for multiple targets
https://github.com/thheller/shadow-cljs
https://github.com/bhauman/lein-figwheel
4.3 – Cursive
• IDE plugin (has one for IntelliJ)
for Clojure projects
• Handy tools such as automatic
bracket insertion
https://cursive-ide.com/
5. Building Web Apps
5.1 – Hiccup Templating
• Templating language using
clojure vectors
• Defined as functions
• Native clojure datastructure
means we can manipulate it as
we please
(defn main-panel
[:div.main-panel
[:h1 "Hello"]
[:div.main-body
[:p "This is hiccup”]]])
https://github.com/weavejester/hiccup
5.2 – React apps with Reagent
• Reagent is a React wrapper
• Uses hiccup templates
• Utilizes atoms as state
(defn main-panel
[]
[:div.main-panel
[:h1 "Hello"]
[:div.main-body
[:p "This is a reagent component”]]])
(defn mount-root []
(reagent/render [main-panel]
(.getElementById js/document "app")))
(defn ^:export init []
(mount-root))
https://reagent-project.github.io/
5.3 – Stateful Reagent component
(defn warning-view
[warning?]
[:div {:class (when @warning? "warning")}
[:span "This is a warning view"]
[:button {:on-click #(swap! warning? not)}]])
(defn main-panel
[]
(let [warning? (reagent/atom false)]
[:div.main-panel
[:h1 "Hello"]
[:div.main-body
[warning-view warning?]]]))
5.4 – Reframe
• App state management system
• Uses Reagent
• Shines for hardcore large-scale
apps
• Similar philosophy to Redux
• Very good docs
https://github.com/Day8/re-frame
5.5 – 1000 Mile view of Reframe
Event
Dispatched
Event
Hander
DBFx Handler
Subscription
View
Subscription
ViewRaw
Subscription
5.6 – Key points on Reframe
• Components don’t know the structure of the db, view
is fully decoupled
• Subs can subscribe to other subs to derive data
• Event handers update the db and trigger side effects
• Raw subscriptions for listening to data-sources outside
of re-frames. Forces all updates to go to db.
Demo Time
6.0 – Other cool things with cljs
• Build react native applications with re-frame
• Shadow cljs can compile node modules to
publish on npm or deploy on lambda functions
7.0 – Want to know more?
• Clojure for the brave and true
• PurelyFunctionalTV Youtube Channel
• Effective Programs by Rich Hickey
https://www.youtube.com/watch?v=2V1FtfBDsLU
• Willing to tell you everything I know over
a beer… https://www.linkedin.com/in/luke-donnet-24699aaa/
The End
Questions?

Exploring Clojurescript

  • 1.
    Exploring ClojureScript Introduction tobuilding apps with ClojureScript Luke Donnet (Github @BadAlgorithm) (In 2019, if you are not doing ClojureScript you are doing JavaScript wrong.)
  • 2.
  • 3.
    1.2 – Aboutme • Software Developer for Adzurra • Current day job stack § React/Redux § Angular 5/Ionic 3 § Clojure § AWS • Have been doing Clojure for ~1 year
  • 4.
    1.3 – “Whyshould I care?” • Something new? • Has made my life easier • Functional Programming
  • 5.
    1.3 – Clojure(.clj) • Part of the LISP (List Processor) Family • Processes code as data • Fully functional programming • Clojure runs on the JVM • Rich Hickey is the Benevolent dictator for life (BDFL)
  • 6.
    1.4 – Syntaxis Easy (+ 1 2) ; equals 3 That’s it! Operator Arguments
  • 7.
    1.5 – ClojureScript(.cljs) • ClojureScript compiles Clojure to JS • Can pretty much run wherever JS can § Web § Nodejs http://clojurescript.org
  • 8.
  • 9.
    2.1 – Basics:Hello world (ns hello-world.core) (def hello-message "Hello world") (defn print-message [message] (print message)) (print-message hello-message) ; Prints "Hello world" - returns nil
  • 10.
    2.2 – Basics:Returning from functions (ns hello-world.core) (def hello-message "Hello world") (defn print-message [message] (print message) "Return me") (print-message hello-message) ; Prints "Hello world" – returns ”Return me"
  • 11.
    2.3 – Types (defa-number 1) (def also-a-number 4.321) (def a-string "I'm a String") (def a-keyword :foo-bar) (def a-namespaced-keyword ::foo-bar) ; turns into :explore-types.core/foo-bar (def a-bool? true) (ns explore-types.core)
  • 12.
    2.4 – DataStructures (def a-map {:key-foo "foo" :key-bar :bar :baz-key a-bool?}) (def a-list '(1 2 3 4 "Hello" :foo a-string)) (def a-vector [:hello "bye" {:foo-bar a-number}]) (def a-set #{1 2 3 4 5 6})
  • 13.
    2.5 – AnonymousFunctions (def a-anon-fn (fn [] "I'm a function")) (def shorthand-function #(print %))
  • 14.
    2.6 – Examplecomparison const result = "foo" === "bar" ? "yea" : "nah"; (def result (if (= "foo" "bar") "yea" "nah")) Ternary operator Destructuring const {foo, bar} = {foo: "foo", bar: "bar", baz: "baz"} (let [{:keys [foo bar]} {:foo "foo" :bar "bar" :baz "baz"}])
  • 15.
    2.7 – Simplein Cljs… no so much in JS Remove multiple keys from object A sum of numbers in a vector Generate a range of values (def my-numbers [1 2 3 4 5 6 7 8 9 10]) (apply + my-numbers) (def my-map {:one "one" :two "two" :three "three" :four "four"}) (dissoc my-map :one :two) ; Or if the list of items to remove is in a vector (def remove-these [:one :two]) (apply (partial dissoc my-map) remove-these) (range 11)
  • 16.
    2.8 – Usefulfunctions The ones we know and love (for [number my-numbers] (println number)) ; Print each value (map inc my-numbers) ; Increment all numbers (filter odd? my-numbers) ; Returns only odd numbers (reduce #(conj (vec %)) [] my-numbers) ; Returns [[1] [2] [3] ...]
  • 17.
    2.9 – Usefulfunctions (cont) The ones we don’t know we love yet… (walk) ; Traverse a datastructure (mapcat) ; concat all items in a vector (interleave) ; Return new collection in alternating order (interpose) ; Insert something in between every item (take-while) ; Take each item while predicate is true (drop-while) ; Remove each item while predicate is true (take-last) ; Get last item (next) ; Get everything expect first item And many more…
  • 18.
    2.10 – Manyuseful functions come built-in • Works out-of-the-box • Limits the number of deps (think of left-pad) • No more scouring stack- overflow for trivial tasks
  • 19.
    2.12 – Macros(might never be in ES*) • Being a Lisp we can use Macros • Extends the language • Clojure which writes clojure… • Use only when needed (defmacro infix "Addition for mere mortals" [infixed] (list (second infixed) (first infixed) (last infixed))) (infix (1 + 1))
  • 20.
    2.13 – Built-inmacro example Threading macro, passes the output of one function into the input of another (->> my-numbers (map inc) (filter odd?) (reduce #(conj (vec %)) [])) Chain of responsibility pattern made simple (reduce #(conj (vec %)) [] (filter odd? (map inc my-numbers)))This Becomes this
  • 21.
    2.14 – Atoms •Immutability? • Nothing will change if nothing changes • Atoms gives us shared state • Changes are synchronous (let [my-val 0] (println my-val) ; 0 (inc my-val) (println my-val)) ; 0 (let [my-val (atom 0)] (println @my-val) ; 0 (swap! my-val inc) (println @my-val)) ; 1 Nothing changes Value changes
  • 22.
    2.15 – UnitTesting • A lot simpler since functions are designed to be pure • No need to integrate with a zillion libraries (deftest validate-form (testing "Should return true if the fields are complete and no fields are empty" (let [mock-signup {:company "my company" :email "email@account.com" :password "thisisapassword"}] (is (not (contains? (validate-signup mock-signup) :response))) (is (every? (validate-signup mock-signup) [:company :email :password])))))
  • 23.
    2.16 – Mockingwith-redef • Mocking is easy • Eliminate the need for complex integration with spies, stubs, mocks… • Out-of-the-box (defn call-me-maybe? [] "Hey, I just met you") (defn test-me [] (call-me-maybe?)) (deftest called? (testing "Check if function is called" (let [count (atom 0)] (with-redefs [call-me-maybe? #(swap! count inc)] (test-me) (is (= @count 1))))))
  • 24.
    2.17 – Furtherreading We’ve only just scratched the surface of Clojure/ClojureScript • Core Async • Cljs and JS interop • Structural sharing (answers: how can immutability be efficient?) • Advanced compilation • Lazy sequences • Transducers (very powerful tool) / Transients • GoF patterns in Clojure http://mishadoff.com/blog/clojure-design-patterns/
  • 25.
  • 26.
    3.1 – Community/Support •Small but active • Many leading star open source projects and devs • Docs are great • Heaps of support through slack/github and others
  • 27.
    3.2 – Cljsin the wild - CircleCI • Great CI/CD system • Use Clojure and ClojureScript full stack
  • 28.
    3.3 – Cljsin the wild - JESI • Journey Management app • Built with reframe and reagent • Manage to operate with a small team (lean and mean)
  • 29.
    3.4 – Cljsin the wild – Roudolph • Christmas light finding app • Completely serverless backend built with Cljs • Mobile app being rebuilt as a PWA with cljs
  • 30.
  • 31.
    4.1 – Leiningen •Build automation and dependency management • Easy to use cli, spin up projects in seconds https://leiningen.org/
  • 32.
    4.2 – Figwheeland Shadow Cljs • Build system for cljs projects • Super fast quick reloading • No need to re-enter form data across reloads • Shadow-cljs excels at ease of use and support for multiple targets https://github.com/thheller/shadow-cljs https://github.com/bhauman/lein-figwheel
  • 33.
    4.3 – Cursive •IDE plugin (has one for IntelliJ) for Clojure projects • Handy tools such as automatic bracket insertion https://cursive-ide.com/
  • 34.
  • 35.
    5.1 – HiccupTemplating • Templating language using clojure vectors • Defined as functions • Native clojure datastructure means we can manipulate it as we please (defn main-panel [:div.main-panel [:h1 "Hello"] [:div.main-body [:p "This is hiccup”]]]) https://github.com/weavejester/hiccup
  • 36.
    5.2 – Reactapps with Reagent • Reagent is a React wrapper • Uses hiccup templates • Utilizes atoms as state (defn main-panel [] [:div.main-panel [:h1 "Hello"] [:div.main-body [:p "This is a reagent component”]]]) (defn mount-root [] (reagent/render [main-panel] (.getElementById js/document "app"))) (defn ^:export init [] (mount-root)) https://reagent-project.github.io/
  • 37.
    5.3 – StatefulReagent component (defn warning-view [warning?] [:div {:class (when @warning? "warning")} [:span "This is a warning view"] [:button {:on-click #(swap! warning? not)}]]) (defn main-panel [] (let [warning? (reagent/atom false)] [:div.main-panel [:h1 "Hello"] [:div.main-body [warning-view warning?]]]))
  • 38.
    5.4 – Reframe •App state management system • Uses Reagent • Shines for hardcore large-scale apps • Similar philosophy to Redux • Very good docs https://github.com/Day8/re-frame
  • 39.
    5.5 – 1000Mile view of Reframe Event Dispatched Event Hander DBFx Handler Subscription View Subscription ViewRaw Subscription
  • 40.
    5.6 – Keypoints on Reframe • Components don’t know the structure of the db, view is fully decoupled • Subs can subscribe to other subs to derive data • Event handers update the db and trigger side effects • Raw subscriptions for listening to data-sources outside of re-frames. Forces all updates to go to db.
  • 41.
  • 42.
    6.0 – Othercool things with cljs • Build react native applications with re-frame • Shadow cljs can compile node modules to publish on npm or deploy on lambda functions
  • 43.
    7.0 – Wantto know more? • Clojure for the brave and true • PurelyFunctionalTV Youtube Channel • Effective Programs by Rich Hickey https://www.youtube.com/watch?v=2V1FtfBDsLU • Willing to tell you everything I know over a beer… https://www.linkedin.com/in/luke-donnet-24699aaa/
  • 44.