http://clojure.org/
https://github.com/clojure/clojurescript
Valentin Waeselynck
@val_waeselynck
Objectives: simplicity, power, focus.
(doseq [msg (for [i (range 100)]
(let [mult-of-3 (= (mod i 3) 0)
mult-of-5 (= (mod i 5) 0)]
(cond
(and mult-of-3 mult-of-5) "fizzbuzz"
mult-of-3 "fizz"
mult-of-5 "buzz"
:else i)
))]
(println msg))
JavaScript
Clojure
f(x);
(f x)
f(x, y, z);
(f x y z)
var Immutable = require('immutable');
var map1 = Immutable.Map({a:1, b:2, c:3});
var map2 = map1.set('b', 12);
print(map1); // => {'a': 1, 'b': 2, 'c': 3}
print(map1); // => {'a': 1, 'b': 12, 'c': 3}
var list1 = Immutable.List.of('a','b','c');
var list2 = list1.push('d');
print(list1); // => ['a', 'b', 'c']
print(list2); // => ['a', 'b', 'c', 'd']
(def map1 {:a 1 :b 2 :c 3})
(def map2 (assoc map1 :b 12))
(def vec1 ["a" "b" "c"])
(def vec2 (conj vec1 "d"))
var Immutable = require('immutable');
var todosState = new Atom(Immutable.List.of("laundry", "grocery"));
function addTodo(newTodo){
todosState.swap(function(todos){
return todos.conj(newTodo);
});
}
function getTodos(){
return todosState.deref();
}
(def todos-state (atom ["grocery" "laundry"]))
(defn get-todos [] @todos-state)
(defn add-todo! [new-todo]
(swap! todos-state (fn [todos] (conj todos new-todo))
))
(doto (new js/XMLHttpRequest)
(.open "GET" "ajax_info.txt" true)
(.send))
(let [x (new js/XMLHttpRequest)]
(.open x "GET" "ajax_info.txt" true)
(.send x)
x)
is as if I had written
var x = new XMLHttpRequest();
x.open("GET","ajax_info.txt",true);
x.send();
return x;
in JS:
our macro
(ns foo.bar
(:require [cljs.core.match :refer-macros [match]]))
(doseq [n (range 100)]
(println
(match [(mod n 3) (mod n 5)]
[0 0] "fizzbuzz"
[0 _] "fizz"
[_ 0] "buzz"
:else n)))
(ns foo.bar
(:require [cljs.core.async :refer [<! >!] :refer-macros [go]]))
(defn load-stories! []
(go (try
(let [story (<! (get-json "story.json"))
{:keys [story chapterURLS]} story
=chapters= (map get-json chapterURLS)]
(doseq [=chapter= =chapters=]
(add-html-to-page! (<! =chapter=)))
(add-text-to-page! "All done"))
(catch js/Error err
(add-text-to-page! (str "Argh, broken: " (:message err))))
)))
async operations
I was recruited to Netscape with the promise of “doing Scheme” in the browser.
- Brendan Eich
JavaScript is LISP in C's clothing.
- Douglas Crockford
ClojureScript is already there
clojure.core
JS interop
Google Closure
λ
state management
(atoms)
syntax extension
(macros)
Big picture
Valentin Waeselynck
@val_waeselynck
42 3.14 -1.2e3 0x0000ff 32r3J2V
true false
"hello world"
"multi
line
string"
nil
:a :key :foo :bar :namespaced.qualified/key ::nq/key
john doe if cond hello-world <tag> even?
(1, 2, 3, 4) ("a" :b c 4 false) (list 1 2 3)
[1 2 3 4] ["a" :b c 4 false] [list 1 2 3]
{:a 1, "b" 2,[:x :y] 3} {:class "fa fa-email" :style "display: none;"}
#{2 3 5 7 11 13}
(ns my.module
; other module
(:require [other.namespace :as other]))
(def x 42) ; global variable
(def y (+ x other/a))
(def z
(let [u (* x 3)] ; local variable
(- u y))
var other = require("other/namespace");
var x = 42;
var y = x + other.a;
var u = x * 3;
var z = u - on.y;
module.exports = {
x: x,
y: y,
z: z
};
(ns my.module)
(def add1 (fn [x] (+ x 1)))
; shorthand for the above
(defn add2 [x]
(+ x 2))
(def add3 #(+ % 3))
; calling the function
(add1 41)
var add1 = function(x){
return x + 1;
}
var add3 = x => x + 3;
// calling the function
add1(41);
module.exports = {
add1: add1,
add3: add3
};
; low-level `if`
(def greeting
(if (= lang :fr)
"Bonjour tout le monde"
"Hello World"))
; higher-level macros
(def greeting2
(cond
(= lang :en) "How are you?"
(and (= lang :fr) several)
"Comment allez-vous ?"
(and (= lang :fr) (not several))
"Comment vas-tu ?"
:je-galere "Hum, do you speak English?"
))
(def greeting3
(case lang
:fr "Bonjour tout le monde"
:de "Guten Tag"
"Hello World"))
;; NOTE: everything is an expression.
var greeting, greeting2, greeting3;
if(lang === 'fr'){
greeting = "Bonjour tout le monde";
} else {
greeting = "Hello world";
}
if(lang === 'en'){
greeting2 = "How are you?";
} else if(lang === 'fr' && several){
greeting2 = "Comment allez-vous ?";
} else if(lang === 'fr' && !several){
greeting2 = "Comment vas-tu ?";
} else {
greeting2 = "Hum, do you speak English?";
}
switch(lang){
case 'fr':
greeting3 = "Bonjour tout le monde"
break;
// [...]
}
(js/alert "coucou")
; access property
(.-innerHtml el)
(aget el "innerHtml")
; set property
(set! (.-innerHtml el) "Coucou")
(aset el "innerHtlm" "Coucou")
; call method
(.log js/console "Coucou")
; create native object
(js-obj "a" 1 "b" 2)
#js {:a 1 :b 2}
; create native array
(array "a" "b" "c")
#js ["a" "b" "c"]
; accessing nested properties is a bit awkward...
(.c (-.b (-.a myObj)) x y)
; ... luckily, we can use a macro!
(-> myObj .-a .-b (.c x y))
alert("coucou")
el.innerHtml
el["innerHtml"]
el.innerHtml = "Coucou";
el["innerHtml"] = "Coucou";
console.log("Coucou")
{a: 1, b: 2}
["a", "b", "c"]
myObj.a.b.c(x,y)
(conj [1 2 3 4 5] 6)
=> [1 2 3 4 5 6]
(map #(* % 2) [1 2 3 4 5])
=> (2 4 6 8 10)
(filter even? [1 2 3 4 5])
=> (2 4)
(reduce + [1 2 3 4 5])
=> 15
(drop 2 [1 2 3 4 5])
=> (3 4 5)
(interleave [:a :b :c :d :e] [1 2 3 4 5])
=> (:a 1 :b 2 :c 3 :d 4 :e 5)
(partition 3 [1 2 3 4 5 6 7 8 9])
=> ((1 2 3) (4 5 6) (7 8 9))
;; using the ->> ('thread-last') macro for chaining
(->> [0 1]
(iterate (fn [[x y]] [y (+ x y)]))
(map first)
(take 12))
=> (0 1 1 2 3 5 8 13 21 34 55 89)
(def m {:a 1 :b 2 :c 3})
(get m :b)
=> 2
; maps are functions of their keys
(m :b)
=> 2
; keywords are function of the maps
(:b m)
=> 2
(assoc m :d 4)
=> {:a 1, :b 2, :c 3, :d 4}
(dissoc m :a )
=> {:a 1 :c 3}
(merge-with + m {:a 2 :b 3})
=> {:a 3, :b 5, :c 3}
(def m2 {:a 1 :b {:c 2 :d 3}})
(get-in m2 [:b :c])
=> 2
(assoc-in m2 [:b :c] 12)
=> {:a 1, :b {:c 12, :d 3}}
(update-in m2 [:b :c] #(* % 3))
=> {:a 1, :b {:c 6, :d 3}}