Refactoring to Macros with Clojure Dimitry Solovyov @dimituri
Let's talk about FUNCTIONAL PROGRAMMING
liftIO  $  atomicModifyIORef  sc  $  n  -­‐>  (n  +  1,  ()) d  <-­‐  f  v  >>:  onInner liftIO  $  ds  `addDisposable`  d
>>:
Let's talk about LISP
LISP in 3 minutes via @bodiltv
object.method(a, b);
object.method( a b)
Clojure      It's a Lisp Compiled Dynamic types Type hints Macros
Clojure        It's a Lisp Compiled Dynamic types Type hints Macros Optional static types Optional Prolog
http://clojure.org/java_interop
HttpServer  server  =  HttpServer.create(address,  0); server.createContext(path,  handler); server.setExecutor(null); server.start();
HttpServer  server  =  HttpServer.create(address,  0); server.createContext(path,  handler); server.setExecutor(null); server.start(); (doto  (HttpServer/create  address  0)    (.createContext  path  handler)    (.setExecutor  nil)    (.start))
macro |ˈmakrəәʊ| noun ( pl. macros ) 1 (also macro instruction) Computing a single instruction that expands automatically into a set of instructions to perform a particular task.
(let*  [G__360  (HttpServer/create  address  0)]    (.createContext  G__360  path  handler)    (.setExecutor  G__360  nil)    (.start  G__360)    G__360) (doto  (HttpServer/create  address  0)    (.createContext  path  handler)    (.setExecutor  nil)    (.start))
根性
.println(Encoding.encodeBase64(Encoding.decodeUrl(Encoding.decodeBas
String  url  =  Encoding.decodeBase64(b); String  decodedUrl  =  Encoding.decodeUrl(url); String  encodedUrl  =  Encoding.encodeBase64(decodedUrl); System.out.println(encodedUrl);
String  url  =  Encoding.decodeBase64(b); String  decodedUrl  =  Encoding.decodeUrl(url); String  encodedUrl  =  Encoding.encodeBase64(decodedUrl); System.out.println(encodedUrl); (-­‐>  b    Encoding/decodeBase64    Encoding/decodeUrl    Encoding/encodeBase64    println)
-­‐> (-­‐>  stuff    (foo  ,,,  a)    (bar  ,,,  b  c  d)    (baz  ,,,  e  f))
-­‐>>-­‐> (-­‐>  stuff    (foo  ,,,  a)    (bar  ,,,  b  c  d)    (baz  ,,,  e  f)) (-­‐>>  stuff    (foo  a  ,,,)    (bar  b  c  d  ,,,)    (baz  e  f  ,,,))
-­‐<>-­‐>>-­‐> (-­‐>  stuff    (foo  ,,,  a)    (bar  ,,,  b  c  d)    (baz  ,,,  e  f)) (-­‐>>  stuff    (foo  a  ,,,)    (bar  b  c  d  ,,,)    (baz  e  f  ,,,)) (-­‐<>  stuff    (foo  a  <>)    (bar  b  c  <>  d)    (baz  <>  e  f))
“Swiss Arrows” -­‐<>> -­‐?<> -­‐!> -­‐!>> -­‐!<> <<-­‐ -­‐< -­‐<:p -­‐<< -­‐<<:p -­‐<>< -­‐<><:p https://github.com/rplevy/swiss-arrows
-­‐<<:p
-­‐<<:p
Q: How do I get the result from a chain of computatoins that may fail?
PhoneNumber  number  =  phoneMap.get(person); if  (number  !=  null)  {        Carrier  carrier  =  carrierMap.get(number);        if  (carrier  !=  null)  {                return  addressMap.get(address);        } } return  null;
WHAT YOU'RE LOOKING FOR IS AN OPTION MONAD
PhoneNumber  number  =  phoneMap.get(person); if  (number  !=  null)  {        Carrier  carrier  =  carrierMap.get(number);        if  (carrier  !=  null)  {                return  addressMap.get(address);        } } return  null; (some-­‐>>    (.get  phoneMap  person)    (.get  carrierMap)    (.get  addressMap))
Clojure vectors implement java.lang.Comparable java.util.RandomAccess
All Clojure functions implement java.util.Comparator java.lang.Runnable java.util.concurrent.Callable
List<Integer>  numbers  =  Arrays.asList(1,  3,  4,  8,  2); Collections.sort(numbers,  new  Comparator<Integer>()  {        @Override        public  int  compare(Integer  o1,  Integer  o2)  {                return  o2.compareTo(o1);        } });
List<Integer>  numbers  =  Arrays.asList(1,  3,  4,  8,  2); Collections.sort(numbers,  new  Comparator<Integer>()  {        @Override        public  int  compare(Integer  o1,  Integer  o2)  {                return  o2.compareTo(o1);        } }); List<Integer>  numbers  =  Arrays.asList(1,  3,  4,  8,  2); Collections.sort(numbers,  (o1,  o2)  -­‐>  o2.compareTo(o1));
List<Integer>  numbers  =  Arrays.asList(1,  3,  4,  8,  2); Collections.sort(numbers,  new  Comparator<Integer>()  {        @Override        public  int  compare(Integer  o1,  Integer  o2)  {                return  o2.compareTo(o1);        } }); (doto  (ArrayList.  [1  3  4  8  2])    (Collections/sort  (fn  [o1  o2]  (.compareTo  o2  o1)))) List<Integer>  numbers  =  Arrays.asList(1,  3,  4,  8,  2); Collections.sort(numbers,  (o1,  o2)  -­‐>  o2.compareTo(o1));
List<Integer>  numbers  =  Arrays.asList(1,  3,  4,  8,  2); Collections.sort(numbers,  new  Comparator<Integer>()  {        @Override        public  int  compare(Integer  o1,  Integer  o2)  {                return  o2.compareTo(o1);        } }); (doto  (ArrayList.  [1  3  4  8  2])    (Collections/sort  (fn  [o1  o2]  (.compareTo  o2  o1)))) (sort  #(compare  %2  %1)  [1  3  4  8  2]) List<Integer>  numbers  =  Arrays.asList(1,  3,  4,  8,  2); Collections.sort(numbers,  (o1,  o2)  -­‐>  o2.compareTo(o1));
(defn  len-­‐reflected  [s]    (.length  s)) (defn  len-­‐hinted  ^String  [^String  s]    (.length  s)) user=>  (time  (dotimes  [i  1000000]  (len-­‐reflected  (str  i)))) "Elapsed  time:  3204.913  msecs" nil user=>  (time  (dotimes  [i  1000000]  (len-­‐hinted  (str  i)))) "Elapsed  time:  113.317  msecs" nil
Polyglot projects with Leiningen (defproject  clojure-­‐java  "1.0.0-­‐SNAPSHOT"    :description  "Example  Clojure+Java  project."    :min-­‐lein-­‐version  "2.0.0"    :source-­‐paths  ["src/clojure"]    :java-­‐source-­‐paths  ["src/java"]    :javac-­‐options  ["-­‐target"  "1.5"  "-­‐source"  "1.5"]) http://leiningen.org
http://www.ldn.lv Workshop: Clojure Saturday, May 18, 2013 10:00 am Citadeles iela 12, Riga http://riga.techhub.com/
(kthx-­‐bye))))))
(defmacro  doto    [x  &  forms]    (let  [gx  (gensym)]        `(let  [~gx  ~x]              ~@(map  (fn  [f]                                (if  (seq?  f)                                    `(~(first  f)  ~gx  ~@(next  f))                                    `(~f  ~gx)))                            forms)              ~gx)))
(ns  foo.core    (:gen-­‐class))   (defn  -­‐main    "I  don't  do  a  whole  lot  ...  yet."    [&  args]    (println  "I'm  a  little  pony")) foo$  lein  run I'm  a  little  pony
(defn  make-­‐example  []    (proxy  [Object]  []        (toString  []  "I'm  a  little  pony"))) user=>  (.toString  (make-­‐some-­‐example)) "I'm  a  little  pony"
class  Example  {        void  someMethod(String  x)  {                someMethod(x,  null)        }          void  someMethod(String  x,  String  y)  {                doSomethingWith(x,  y);        } } (proxy  [Example]  []    (toString        ([x]      (proxy-­‐super  someMethod  x))        ([x  y]  (do-­‐other-­‐stuff  this  x  y))))
(let  [^LoadingCache  cache  (doto  CacheBuilder/newBuilder                            (.maximumSize  1000)                            (.build  (reify  CacheLoader                                                (load  ^Graph  [^Key  key]  (create-­‐expensive-­‐graph  key)))))]) LoadingCache<Key,  Graph>  cache  =  CacheBuilder.newBuilder()                .maximumSize(1000)                .build(new  CacheLoader<Key,  Graph>()  {                              public  Graph  load(Key  key)  {                                      return  createExpensiveGraph(key);                              }                      }); LoadingCache<Key,  Graph>  cache  =  CacheBuilder.newBuilder()                .maximumSize(1000)                .build((key)  -­‐>  createExpensiveGraph(key));

Refactoring to Macros with Clojure

  • 1.
    Refactoring to Macros withClojure Dimitry Solovyov @dimituri
  • 2.
  • 4.
    liftIO  $  atomicModifyIORef  sc  $  n  -­‐>  (n  +  1,  ()) d  <-­‐  f  v  >>:  onInner liftIO  $  ds  `addDisposable`  d
  • 8.
  • 9.
  • 10.
    LISP in 3minutes via @bodiltv
  • 11.
  • 12.
  • 14.
  • 15.
    Clojure        It's a Lisp Compiled Dynamictypes Type hints Macros Optional static types Optional Prolog
  • 16.
  • 17.
    HttpServer  server  =  HttpServer.create(address,  0); server.createContext(path,  handler); server.setExecutor(null); server.start();
  • 18.
    HttpServer  server  =  HttpServer.create(address,  0); server.createContext(path,  handler); server.setExecutor(null); server.start(); (doto  (HttpServer/create  address  0)    (.createContext  path  handler)    (.setExecutor  nil)    (.start))
  • 19.
    macro |ˈmakrəәʊ| noun (pl. macros ) 1 (also macro instruction) Computing a single instruction that expands automatically into a set of instructions to perform a particular task.
  • 20.
    (let*  [G__360  (HttpServer/create  address  0)]    (.createContext  G__360  path  handler)    (.setExecutor  G__360  nil)    (.start  G__360)    G__360) (doto  (HttpServer/create  address  0)    (.createContext  path  handler)    (.setExecutor  nil)    (.start))
  • 21.
  • 22.
  • 23.
    String  url  =  Encoding.decodeBase64(b); String  decodedUrl  =  Encoding.decodeUrl(url); String  encodedUrl  =  Encoding.encodeBase64(decodedUrl); System.out.println(encodedUrl);
  • 24.
    String  url  =  Encoding.decodeBase64(b); String  decodedUrl  =  Encoding.decodeUrl(url); String  encodedUrl  =  Encoding.encodeBase64(decodedUrl); System.out.println(encodedUrl); (-­‐>  b    Encoding/decodeBase64    Encoding/decodeUrl    Encoding/encodeBase64    println)
  • 25.
    -­‐> (-­‐>  stuff    (foo  ,,,  a)    (bar  ,,,  b  c  d)    (baz  ,,,  e  f))
  • 26.
    -­‐>>-­‐> (-­‐>  stuff    (foo  ,,,  a)    (bar  ,,,  b  c  d)    (baz  ,,,  e  f)) (-­‐>>  stuff    (foo  a  ,,,)    (bar  b  c  d  ,,,)    (baz  e  f  ,,,))
  • 27.
    -­‐<>-­‐>>-­‐> (-­‐>  stuff    (foo  ,,,  a)    (bar  ,,,  b  c  d)    (baz  ,,,  e  f)) (-­‐>>  stuff    (foo  a  ,,,)    (bar  b  c  d  ,,,)    (baz  e  f  ,,,)) (-­‐<>  stuff    (foo  a  <>)    (bar  b  c  <>  d)    (baz  <>  e  f))
  • 28.
    “Swiss Arrows” -­‐<>> -­‐?<>-­‐!> -­‐!>> -­‐!<> <<-­‐ -­‐< -­‐<:p -­‐<< -­‐<<:p -­‐<>< -­‐<><:p https://github.com/rplevy/swiss-arrows
  • 29.
  • 30.
  • 31.
    Q: How doI get the result from a chain of computatoins that may fail?
  • 32.
    PhoneNumber  number  =  phoneMap.get(person); if  (number  !=  null)  {        Carrier  carrier  =  carrierMap.get(number);        if  (carrier  !=  null)  {                return  addressMap.get(address);        } } return  null;
  • 33.
    WHAT YOU'RE LOOKINGFOR IS AN OPTION MONAD
  • 35.
    PhoneNumber  number  =  phoneMap.get(person); if  (number  !=  null)  {        Carrier  carrier  =  carrierMap.get(number);        if  (carrier  !=  null)  {                return  addressMap.get(address);        } } return  null; (some-­‐>>    (.get  phoneMap  person)    (.get  carrierMap)    (.get  addressMap))
  • 36.
  • 37.
    All Clojure functionsimplement java.util.Comparator java.lang.Runnable java.util.concurrent.Callable
  • 38.
    List<Integer>  numbers  =  Arrays.asList(1,  3,  4,  8,  2); Collections.sort(numbers,  new  Comparator<Integer>()  {        @Override        public  int  compare(Integer  o1,  Integer  o2)  {                return  o2.compareTo(o1);        } });
  • 39.
    List<Integer>  numbers  =  Arrays.asList(1,  3,  4,  8,  2); Collections.sort(numbers,  new  Comparator<Integer>()  {        @Override        public  int  compare(Integer  o1,  Integer  o2)  {                return  o2.compareTo(o1);        } }); List<Integer>  numbers  =  Arrays.asList(1,  3,  4,  8,  2); Collections.sort(numbers,  (o1,  o2)  -­‐>  o2.compareTo(o1));
  • 40.
    List<Integer>  numbers  =  Arrays.asList(1,  3,  4,  8,  2); Collections.sort(numbers,  new  Comparator<Integer>()  {        @Override        public  int  compare(Integer  o1,  Integer  o2)  {                return  o2.compareTo(o1);        } }); (doto  (ArrayList.  [1  3  4  8  2])    (Collections/sort  (fn  [o1  o2]  (.compareTo  o2  o1)))) List<Integer>  numbers  =  Arrays.asList(1,  3,  4,  8,  2); Collections.sort(numbers,  (o1,  o2)  -­‐>  o2.compareTo(o1));
  • 41.
    List<Integer>  numbers  =  Arrays.asList(1,  3,  4,  8,  2); Collections.sort(numbers,  new  Comparator<Integer>()  {        @Override        public  int  compare(Integer  o1,  Integer  o2)  {                return  o2.compareTo(o1);        } }); (doto  (ArrayList.  [1  3  4  8  2])    (Collections/sort  (fn  [o1  o2]  (.compareTo  o2  o1)))) (sort  #(compare  %2  %1)  [1  3  4  8  2]) List<Integer>  numbers  =  Arrays.asList(1,  3,  4,  8,  2); Collections.sort(numbers,  (o1,  o2)  -­‐>  o2.compareTo(o1));
  • 43.
    (defn  len-­‐reflected  [s]    (.length  s)) (defn  len-­‐hinted  ^String  [^String  s]    (.length  s)) user=>  (time  (dotimes  [i  1000000]  (len-­‐reflected  (str  i)))) "Elapsed  time:  3204.913  msecs" nil user=>  (time  (dotimes  [i  1000000]  (len-­‐hinted  (str  i)))) "Elapsed  time:  113.317  msecs" nil
  • 44.
    Polyglot projects with Leiningen (defproject  clojure-­‐java  "1.0.0-­‐SNAPSHOT"    :description  "Example  Clojure+Java  project."    :min-­‐lein-­‐version  "2.0.0"    :source-­‐paths  ["src/clojure"]    :java-­‐source-­‐paths  ["src/java"]    :javac-­‐options  ["-­‐target"  "1.5"  "-­‐source"  "1.5"]) http://leiningen.org
  • 45.
    http://www.ldn.lv Workshop: Clojure Saturday, May18, 2013 10:00 am Citadeles iela 12, Riga http://riga.techhub.com/
  • 46.
  • 47.
    (defmacro  doto    [x  &  forms]    (let  [gx  (gensym)]        `(let  [~gx  ~x]              ~@(map  (fn  [f]                                (if  (seq?  f)                                    `(~(first  f)  ~gx  ~@(next  f))                                    `(~f  ~gx)))                            forms)              ~gx)))
  • 48.
    (ns  foo.core    (:gen-­‐class))   (defn  -­‐main    "I  don't  do  a  whole  lot  ...  yet."    [&  args]    (println  "I'm  a  little  pony")) foo$  lein  run I'm  a  little  pony
  • 49.
    (defn  make-­‐example  []    (proxy  [Object]  []        (toString  []  "I'm  a  little  pony"))) user=>  (.toString  (make-­‐some-­‐example)) "I'm  a  little  pony"
  • 50.
    class  Example  {        void  someMethod(String  x)  {                someMethod(x,  null)        }          void  someMethod(String  x,  String  y)  {                doSomethingWith(x,  y);        } } (proxy  [Example]  []    (toString        ([x]      (proxy-­‐super  someMethod  x))        ([x  y]  (do-­‐other-­‐stuff  this  x  y))))
  • 51.
    (let  [^LoadingCache  cache  (doto  CacheBuilder/newBuilder                            (.maximumSize  1000)                            (.build  (reify  CacheLoader                                                (load  ^Graph  [^Key  key]  (create-­‐expensive-­‐graph  key)))))]) LoadingCache<Key,  Graph>  cache  =  CacheBuilder.newBuilder()                .maximumSize(1000)                .build(new  CacheLoader<Key,  Graph>()  {                              public  Graph  load(Key  key)  {                                      return  createExpensiveGraph(key);                              }                      }); LoadingCache<Key,  Graph>  cache  =  CacheBuilder.newBuilder()                .maximumSize(1000)                .build((key)  -­‐>  createExpensiveGraph(key));