Why learn yet another lang?
do more with less
Why learn functional lang?
var coll = [1, 2, 3, 4, 5, 6]; var acc = []; for(var i = 0; i< coll.length && acc.length < 3; i++){ if(i%2 == 1){ acc.push(coll[i] + 1) } }
var inc = function(x){ return x + 1; } var odd = function(x){ return x%2 == 1; } acc = [1, 2, 3, 4, 5, 6].map(inc).filter(odd).take(2);
Which functional lang?
What is LISP?
(= (type nil) nil) (= (type 100) java.lang.Long) (= (type 0.1) java.lang.Double) (= (type "hi") java.lang.String) (= (type #"^a.*") java.util.regex.Pattern) (= (type :orange) clojure.lang.Keyword) (= (type 'apple) clojure.lang.Symbol)
(= (type '(1 2 3)) clojure.lang.PersistentList) (= (type [1 2 3]) clojure.lang.PersistentVector) (= (type {:a 1 :b 2}) clojure.lang.PersistentArrayMap) (= (type #{1 2 3}) clojure.lang.PersistentHashSet)
Syntax?
function myfn(x,y) | ["function", "myfn", { | ["x", "y"], return | ["+", x*x + y*y; | ["*", "x", "x"], } | ["*", "y", "y"]]] | myfn(3,6) | ["myfn", "3", "6"]
["function", "myfn", | [function myfn ["x", "y"], | [x y] ["+", | [+ ["*", "x", "x"], | [* x x] ["*", "y", "y"]]] | [* y y]]] | ["myfn", "3", "6"] | [myfn 3 6]
[function myfn [x y] | (defn myfn [x y] [+ [* x x] | (+ (* x x) [* y y]]] | (* y y))) | [myfn 3 6] | (myfn 3 6)
function myfn(x,y){ | (defn myfn [x y] return x*x + y*y; | (+ (* x x) }; | (* y y))) | myfn(3,6) | (myfn 3 6) // 12 [{;,}] | ;; 12 ([])
(special-form args) (function-call args) (macros-call args)
just lists
(def symbol init?) (if test then else?) (do exprs*) (let [bindings* ] exprs*) (quote form) (var symbol) (fn name? [params* ] exprs*) (loop [bindings* ] exprs*) (recur exprs*) (throw expr) (try expr* catch-clause* finally-clause?)
(defn inc [x] (+ 1 x)) ;; (def inc (fn [x] ..)) (myfn 1 2) (defn bar "doc string?" ;; embed doc ([a b] ...) ;; multi arity ([a b c] ...) (defn constrained-fn [f x] {:pre [(pos? x)] ;; pre & post conditions :post [(= % (* 2 x))]} ;; contracts (f x)) (defn myfn [{a :a}] ;; destructuring ...)
(defmacro unless [pred f1 f2] (list 'if (list 'not pred) f1 f2)) (defmacro unless [pred f1 f2] `(if (not (~pred)) ~f1 ~f2)) (unless (> 5 (rand 10)) "ok" "not ok")) (if (not (> 5 (rand 10))) "ok" "not ok"))
(defn myfn [args] body) ;; (def myfn (fn []..)) (cond pred1 expr1 ;; (if pred1 expr (cond ...)) pred2 expr2) (for [x xs] (* x x)) ;; (loop [... (doseq [x xs] (println x)) ;; (loop [..
&
оперируют значениями (value)
function fullName(first, middle, last){ return first + (middle[0] ? ( ' ' + middle[0]) : '') + ',' + last; } fullName('Nicola', 'Nikolaevich', 'Ryzhikov') //=> Nicola N. Ryzhikov
никаких скрытых зависимостей
все явно
function myfn(somearray){ var copy = somearray.clone() return copy.push('ups'); }
Персистентные
структуры данных O(1)
fixed price cpu/memory
можно передвать функции в функции и возвращать функции из функций
(map :name users) => ["nicola" "pavel" "marat"] (filter odd? (range 10)) => [1 3 5 7 9] (reduce + 0 (range 100)) => 100 (reduce * 1 (range 10)) => 10! (->> coll (map inc) (filter odd) (take 5)) (take 5 (filter odd (map inc coll)))
(defn handler [x] (/ 1 x)) (defn wrap [f] (fn [x] (if (< x 0) nil (f x)))) (def stack (wrap handler)) (stack 1) => 1 (stack 2) => 1/2 (stack 0) => null
chain of responsibility
(reduce (fn [acc x] (+ acc x)) 0 [1 2 3 4]) (reduce (fn [acc x] (conj acc (+ x 1)) [] [1 2 3 4]) (def transducer (fn [f] (fn [acc c] (if cond (f acc c) acc)))) (def trans (-> (map inc) (filter odd?) (take 5))) (trans (range 100)) => [1 3 5 7 9]
nREPL server started on 54903 port > (start sys) > (doc map) > (inspect sys) > (stop sys) > (run-tests) > (eval ...)
vim: fireplace
(into [1 2] [3 4]) ;=> [ 1 2 3 4] (into {:a 1 :b 2} {:c 4}) ;=> {:a 1 :b 2 :c 3}
(defprotocol Speaker (say [this])) (deftype Rubist [name] Speaker (say [this] "ruby")) (deftype Clojurist [name] Speaker (say [this] "clojure")) (for [x [(Rubyist "nicola") (Clojurist "nicola")] (say x))
(defmulti encounter (fn [x y] [(:Species x) (:Species y)])) (defmethod encounter [:Bunny :Lion] [b l] :run-away) (defmethod encounter [:Bunny :Bunny] [b l] :sex)
(extend-protocol ICoerce Date (to-date-time [date] (from-date date)) java.sql.Date (to-date-time [sql-date] (from-sql-date sql-date)) java.sql.Timestamp (to-date-time [sql-time] (from-sql-time sql-time)))
State & Identity
(def state (atom {})) (deref state) ;; or @state (swap! state (fn [old] (assoc old :key "val"))) @state
(def counter (agent 0)) @counter (send counter inc) ;;(send counter (fn [old] (+ 1 old)) @counter ;; may be 1
(def account-a (ref 1000)) (def account-b (ref 1000)) ;; transaction (dosync (alter account-a + 100) (alter account-b - 100))
(let [c1 (upload "serious.jpg") c2 (upload "fun.jpg") c3 (upload "sassy.jpg") [res chan] (alts!! [c1 c2 c3 (timeout 20)])] (report res))
(.toUpperCase "fred") (.getName String) (.-x (java.awt.Point. 1 2)) (System/getProperty "java.vm.version") Math/PI