Thinking in Data
Priyatam Mudivarti
Code manipulates data. To understand code, a learner must see the data, and see the effect of code on the data.
- Brett Victor
Complex?
Templates
client apis
Build Tools
Responsive Design
Events
Dom
Performance
Modules
3rd party libs
Javascript
event handling, dynamic, ajax, json
CSS3/Less/Sass
rules, selectors,
responsive design
HTML5
markup , templating, dom
Simpler
A typical full stack application has hundreds of classes, getters/setters, state, tens of thousands of lines of code
Clojure code has no classes
no global state
1/10th to 1/100th of the codebase
Clojure
in 5 mins
data
functions
(def a-vector [1 2 3 4 5])
(def a-nested-map
{:customer-id 1e6
:preferences {:nickname "Bob"
:avatar "http://en.gravatar.com/userimage/0/0.jpg"}
:services {:alerts {:daily true}}})
(def complex-map
{[1 2] :one-two
[3 4] :three-four})
(def a-set
#{:cat :dog :bird})
Clojurescript 202: immutable data structures
;; Unlike JavaScript there is no hoisting in ClojureScript. ClojureScript has lexical scoping.
(def some-x 1)
(let [some-x 2]
some-x)
;; functions parameters, let bindings locals, and loop locals are not mutable!
(let [fns (loop [i 0 ret []]
(if (< i 10)
(recur (inc i) (conj ret (fn [] i)))
ret))]
(map #(%) fns))
Clojurescript 202: scope
; simple function
(defn add [a b]
(+ a b))
;; function with multiple args and default values
(defn another-function
([x] (defaults x :default))
([x y] [x y]))
;; closure
(let [a 1e3]
(defn foo []
(* a a))
(defn bar []
(+ (foo) a)))
;; higher order functions
Clojurescript 202: functions
;; list comprehension
(for [x (range 1 10)
y (range 1 10)
:when (and (zero? (rem x y))
(even? (quot x y)))]
[x y])
;; higher order functions over sequences
(map inc [0 1 2 3 4 5 6 7 8 9])
(filter even? (range 10))
(reduce + (range 100))
(partition 2 [:a 1 :b 2 :c 3 :d 4 :e 5])
(remove odd? (range 10))
(take 5 (interleave (repeat "red") (repeat "blue")))
;; Protocols, a.k,a Interfaces
(defprotocol MyProtocol (awesome [this]))
(extend-protocol MyProtocol
js/Date
(awesome [_] "Having an awesome time!")
number
(awesome [_] "I'm an awesome number!"))
(awesome #inst "2014")
(awesome 5)
Clojurescript 202: abstractions
That's it.
can we build http services with these concepts?
No More framework abstractions
HTTP services in Clojure (Ring)
;; handler
(defn my-ip [request]
{:status 200
:headers {"Content-Type" "text/plain"}
:body (:remote-addr request)})
;; middleware
(defn wrap-content-type [handler content-type]
(fn [request]
(let [response (handler request)]
(assoc-in response [:headers "Content-Type"] content-type))))
(defn wrap-content-type [handler content-type]
(fn [request]
(let [response (handler request)]
(assoc-in response [:headers "Content-Type"] content-type))))
(defn json-response [data & [status]]
{:status (or status 200)
:headers {"Content-Type" "application/edn"}
:body (pr-str data)})
(def app
(wrap-content-type my-ip "text/html"))
(defn serve [request]
(app request))
Ring - Concepts
;; Routes
(defroutes routes
(GET "/" [] (str "Server is running"))
(GET "/ws" [] handle-websocket)
(context
"/api" []
(GET "/languages" []
(let [prog (get-in db/data [:languages])]
(when prog
(edn-response prog))))
(GET "/languages/:id/:type" [id type]
(let [stats (filter #(and
(= (:id %) id) (= (:type %) type))
(get-in db/data [:stats]))]
(when stats
(response stats)))))))
Routes
(def app
(->
(handler/site router/routes)
(wrap-json-body)
(wrap-json-response)
(wrap-edn-params)
(wrap-defaults api-defaults)
(wrap-cors
:access-control-allow-origin #".+"
:access-control-allow-methods [:get :post :put :delete :head]
:access-control-allow-headers ["Content-Type" "X-Requested-With"])))
(defn -main [& _]
(let [port (Integer. (or (System/getenv "SERVER_PORT") 8000))]
(server/run-server app {:port port :join? false})
(log/info "Server started and listening at port..." port)))
Server
HTTP Services
Abstract HTTP
Write idiomatic code
Run on any web server
Bare Metal
Easy to Test
Live Coding
I've written webservices in 5 languages.
I find Clojure to be 10x simpler
Let's talk about CSS/SASS
no namespaces
can't compose HTML and CSS in the same syntax
css doesn’t “talk to dom”
math impl is hard
can we pass css as functions at runtime?
mixins are not composable
constrained by limitations—not by choice
(defn create-minimal-grid [clazz pad]
[[:* {:box-sizing "border-box"}]
[clazz {:background "white"
:margin [[0 0 pad 0]]}
[:&:after {:content ""
:display "table"
:clear "both"}]
["[class*='col-']" {:float "left"
:padding-right pad }]
[:.col-1-3 {:width "33.33%"}]
[:.col-2-3 {:width "66.66%"}]
[:.col-1-2 {:width "50.00%"}]
[:.col-1-4 {:width "25.00%"}]
[:.col-1-8 {:width "12.50%"}]
[:.out-padding {:padding [[pad 0 pad pad pad]]}
["[class*='col-']:last-of-type"
{:padding-right pad}]]]])
A grid in a single function
(defn default-layout
"Creates a tiled layout with n 'views' stacked on top, with a help
view floated to the right.
For more info see https://bulma.io/documentation/layout/tiles/"
[help-section & views]
[:div {:class "tile is-ancestor"}
[:div {:class "tile is-1 is-vertical"}]
[:div {:class "tile is-7 is-vertical is-parent"}
(for [view views]
^{:key view} [:div {:class "tile is-child"}
view])]
[:div {:class "tile is-2 is-parent"}
help-section]])
Sample layout
It is better to have 100 functions operate on one data structure than to have 10 functions operate on 10 data structures
- Alan J. Perlis
Thinking in Data
By Priyatam Mudivarti
Thinking in Data
Thinking in Data. Clojure/Cljs Web development
- 377