Functional Programming in Clojurescript

Priyatam Mudivarti

Principal, Facjure 

 

Understanding

REPL

Understanding (parens)

Data Structures

Functions, Closures, HoF

Data Flow

 

Exploring

HTML5

CSS3

Templating

IDEs

Libraries

Agenda

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

How?

FP in Cljs

my version, anyway.

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

A data structure is just a stupid programming language
 

— R. Wm. Gosper

Data
Composition
Abstractions

Really, there's nothing else

You don't have to worry about

build plugins

mvc frameworks

modules

load-times

bundling

3rd party "functional" libs

optimization (dead code elimination)

Data structures in Cljs

immutable, lazy sequences 

functions that create sequences

functions that filter sequences

sequence predicates

functions that transform sequences

;; 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))

Scopes

 

; 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

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})

Basic Data structures

 

(defn clearfix [clazz]
  [clazz {:*zoom 1}
   [:&:before :&:after {:display "table"
                        :content " "
                        :line-height 0}]
   [:&:after {:clear "both"}]])

CSS as Data

[
 {:poem/title "I am not a hacker"}
 {:poem/book :poets-and-hackers}
 {:poem/year 2015}
 {:author/name "Priyatam Mudivarti"}
 {:author/email "priyatam@facjure.com"}
 {:author/gender :male}
 {:tag/names #{:hackers :poetry-slam :boulder-cafe :fp-rocks}
 {[1 2] :one-two }}
]

Edn, a data transfer format

[
 {:poem/title "I am not a hacker"}
 {:poem/book :poets-and-hackers}
 {:poem/year 2015}
 {:author/name "Priyatam Mudivarti"}
 {:author/email "priyatam@facjure.com"}
 {:author/gender :male}
 {:tag/names #{:hackers :poetry-slam :boulder-cafe :fp-rocks}
 {[1 2] :one-two }}
]

Json as Data


(def header-snippet
  [:header
   [:h1 "Mala"]
   [:h3 "A User Interface template in Clojurescript."]])

(def footer-snippet
  [:footer
   [:a {:href "https://twitter.com/priyatam"} "@priyatam"]])


(defn view [{:keys [active path name]}]
  (html
   [:section {:class "home"}
    header-snippet
    [:nav
     [:ul
      [:li {:class (if active "active" "")}
       [:a {:href (str "#" path)} name]]]]
    footer-snippet]))

HTML/Templating as Data

;; Plain old Ajax

(defn handler [response]
  (.log js/console (str response)))

(defn error-handler [{:keys [status status-text]}]
  (.log js/console (str "something bad happened: " status " " status-text)))

(GET "/hello" {:handler handler
               :error-handler error-handler})


;; Core.Async

(defn GET [res handler]
  (go (let [response (<! (http/get res {:with-credentials? false}))]
        (logp (:body response))
        (handler (:body response)))))

Ajax / Async Http as Data

(ns brutha.example
  (:require [brutha.core :as br :refer [mount]]
            [sablono.dom :as dom]))
            
(mount 
  (html [:p "Hello World"]) 
  (js/document.getElementById "app"))            
            

React components as Data

;; list comprehension
(for [x (range 1 10)
      y (range 1 10)
      :when (and (zero? (rem x y))
                 (even? (quot x y)))]
  [x y])

List comprehensions

 

;; 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")))

Higher-order functions

 

 

Abstraction is at the center of most work in
Computer Science &
User Interface Design.

 

Abstractions

a lot more

transducers, pmap, comp ...

(map
   (comp - (partial + 3) (partial * 2))
       [1 2 3 4])
;;=>  (-5 -7 -9 -11)

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

programming stylesheets
in FP

 

separate  selectors and declarations

 

Ring-style middleware

Mesh, a grid & typography library

http://github.com/facjure/mesh



(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

So

everything is Data

a sequence of values

pipe, compose

 

Exercise #1

a simple autocomplete

markup in cljs data structures

styles in clojurescript data structures

event handling as functions

Exercise #3

Om/React

markup in cljs data structures

styles in clojurescript data structures

event handling as functions

Exercise #3

markdown editor

single-page app with html5 local-storage

Exercise #4

Responsive Design

css3

References

Demos created with
https://github.com/priyatam/mala
https://github.com/facjure/mesh

Libraries
https://github.com/omcljs/om
https://github.com/noprompt/garden
https://github.com/noprompt/secretary
https://github.com/bhauman/lein-figwheel

https://slides.com/priyatam/functional-programming-with-cljs

 

 

 

@priyatam    

priyatam@facjure.com

http://github.com/priyatam

Functional Programming in Cljs

By Priyatam Mudivarti

Functional Programming in Cljs

Clojurescript is an FP language that targets the Browser, Node, and Rhino. As a subset of Clojure, Cljs compiles to an optimized Js, and provides a solid foundation for building and designing user-interfaces with immutable data structures, first-class namespaces, scopes, and a minimalist syntax that does more with less.

  • 396