INTEROPERABILITY CLOJURE PYTHON Enrico Franchi
2 OUTLINE Why Clojure? Why Java? Clojure from 3000 m. Jython-Clojure interoperability Clojure-Jython interoperability ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
3 GENERAL NOTES During this presentation some very explicit Java code may be shown. No ducks were harmed during the making of this presentation. ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
4 JVM LANGUAGES Jython Java ? JVM ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
5 JVM LANGUAGES Jython Java Clojure JVM ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
6 CLOJURE Clojure is Lisp Clojure is a good Lisp Clojure has a more functional flavor than Common Lisp; stateful programming is banned unless in very controlled ways Clojure lives on the JVM and perhaps its rejection of state is a reaction to the heavily stateful model of JVM Clojure is not a pure functional language by any means ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
7 JVM LANGUAGES Jython Implementation of Python in Java Jython calls Java ~ Clojure New Programming Languages built on the JVM Design choices in Clojure reflect design choices of the JVM Interoperability ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
8 CLOJURE ~ Good Parts ~ Functional Programming Laziness Full Macros Multi-Methods Immutable types, STM & Concurrency Model ~ Bad Parts ~ “Java” ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
9 ATOMIC TYPES Integers Floating Point Numbers Ratios (3/4 is not 0.75, is ¾) BigDecimal Strings (“foo”) Booleans (true, false) Nil (nil) Characters (a, b) :keywords regexp’s (#“foo.*”) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
10 SEQUENCE TYPES Python Common Lisp Clojure Type Syntax Type Syntax Type Syntax Random access list [1, 2] vector #(1 2) vector [1 2] sequence Linked List - No list (1 2) list (1 2) Set set {1, 2} No No set #{1 2} Map dict {1:2, 3:4} hash-table No vector {1 2, 3 4} Clojure collections In Clojure all collections are immutable; implement corresponding all the functions return a new collections – as usual, Java collections interfaces immutability allows easy data sharing – vector, list List set Set map Map ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
11 CLOJURE EXAMPLE (defn rember [a lat]   (cond    (empty? lat) '()    (= (first lat) a) (rest lat)    :else (cons           (first lat)           (rember a (rest lat))))) (rember 4 '(1 2 3 4 5 6 4)) ; => (1 2 3 5 6 4) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
12 TAIL CALL OPTIMIZATION (defn multirember [a lat]   (letfn       [(multirember-aux [source sink]                    (if (seq source)                      (if (= (first source) a)                        (recur (rest source) sink)                        (recur (rest source)                               (conj sink (first source))))                      sink))]     (multirember-aux lat []))) (take 10 (multirember 4 '(1 2 3 4 5 6 4)) (multirember 4 (iterate inc 0))) ; => [1 2 3 5 6] ; Evaluation aborted. ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
13 LAZINESS (defn lazy-multirember [a lat]   (letfn       [(multirember-aux [source]                         (lazy-seq                          (if (seq source)                            (if (= (first source) a)                              (multirember-aux (rest source))                              (cons (first source)                                    (multirember-aux (rest source))))                           '())))]     (multirember-aux lat))) (take 10 (lazy-multirember 4 (iterate inc 0))) ; => (0 1 2 3 5 6 7 8 9 10)
14 NAMESPACES & MODULES (ns example.namespace) (ns example.namespace (:require clojure.set)) Introduces a new namespace. With def (and defn, …) stuff is (ns example.namespace added to namespaces. (:require [clojure.set :as s])) (ns example.namespace (ns example.namespace (:use clojure.string)) (:import [java.util HashMap])) (ns example.namespace (:use [clojure.string :only [capitalize]])) (ns example.namespace (:use [clojure.string :exclude [capitalize]]) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
15 CALLING JAVA Java Clojure Jython import app.model.Person; (import [app.model Person]) from app.model import Person new Person() (new Person) Person() (Person.) person.getFullName() (. getFullName person) person.getFullName() (.getFullName person) Locale.JAPAN (. Locale JAPAN) Locale.JAPAN Locale/JAPAN Integer.valueOf(“42”) (. Integer valueOf "42") java.lang.Integer.valueOf('42') (Integer/valueOf “42”) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
16 PROXIES (ns gui-sample   (:import [javax.swing JFrame JButton] (gui-sample/build-gui            [java.awt.event ActionListener])) "Rock & Roll" (defn build-gui [title message] "Hello, world!”)   (let [frame (JFrame. title)         button (JButton. "CLICK")]     (.addActionListener button                         (proxy [ActionListener] []                           (actionPerformed [evt]                                            (println message))))     (.. frame getContentPane (add button))     (.pack frame)     (.setVisible frame true)     frame)) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
17 PROXIES (ns gui-sample   (:import [javax.swing JFrame JButton]            [java.awt.event ActionListener])) (defn build-gui [title message]   (let [frame (JFrame. title)         button (JButton. "CLICK")         px (proxy [ActionListener] []                           (actionPerformed [evt]                                            (println message)))]     (.addActionListener button px)     (.. frame getContentPane (add button))     (.pack frame)     (.setVisible frame true) px))      (update-proxy p {"actionPerformed" (fn [this evt] (println "foo!"))}) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
18 GEN-CLASS (ns example.ClassExample   (:gen-class    :name example.ClassExample    :extends Object    :implements []    :methods [ [foo [java.util.Collection] int]              ^{:static true} [show [java.util.Collection] void]])   (:import (java.util Collection))) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
19 GEN-CLASS (2) (defn show [coll] (defn -show [coll]   (if (seq coll)   (show coll))     (do       (print (first coll)) (defn -foo [this coll]       (recur (rest coll)))   (let [coll (seq coll)]     (println "")))     (if coll (count coll) -1))) >>> import example >>> example.ClassExample.show([1, 2, 3]) 123 >>> ce = example.ClassExample() >>> ce.foo([1, 2, 3]) 3
20 IMPLEMENTING PYOBJECTS IN CLOJURE import clj (ns clj.ImmutableList (:gen-class py_list = ['a', 'b', 'c'] :name clj.ImmutableList my_list = clj.ImmutableList(py_list) :extends org.python.core.PyObject :implements [clojure.lang.IPersistentList] :state state :init init :constructors {[java.util.Collection], []}) (:import [org.python.core PyObject PyInteger Py])) (defn -init [coll] [[coll] (apply list coll)]) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
21 IMPLEMENTING PYOBJECTS IN CLOJURE (2) (defmacro delegate-to-state [sym] (defn -cons [this other] `(defn ~(symbol (str "-" sym)) (cons (.state this) other)) [this#] (~sym (.state this#)))) (defn -equiv [this other] print(.state this) other)) (= my_list.peek() (delegate-to-state peek) print my_list.pop() (delegate-to-state pop) print my_list.count() (delegate-to-state count) print my_list.empty() (delegate-to-state empty) print my_list.seq() (delegate-to-state seq) print my_list.cons('d') print my_list.equiv(py_list) print my_list.equiv(['a', 'b', 'c']) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
22 IMPLEMENTING PYOBJECTS print my_list[0] IN CLOJURE (3) print my_list[1] print my_list[2] (defn -__finditem__ [this index] print my_list[2.4] (let [index (if (instance? Number index) index try: (Py/tojava index Number))] print my_list[3] except IndexError, e: (try print e (Py/java2py (nth (.state this) index)) try: (catch IndexOutOfBoundsException e print my_list['a'] (throw (Py/IndexError (.toString e))))))) except TypeError, e: print e try: my_list[0] = 1 except TypeError, e: print e ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
23 CLOJURE RT from clojure.lang import RT, Compiler source = ''' (ns rember) Compiler.load(java.io.StringReader(source)) (defn rember [a lat] rember = RT.var('rember', 'rember') (cond print rember.invoke(2, range(4)) (empty? lat) '() (= (first lat) a) (rest lat) :else (cons (first lat) (rember a (rest lat))))) ''' ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
24 CLOJURE DECORATOR import pyclj @pyclj.clojure def rember(a, lat):     ’’’(defn rember "Remove first occurrence of a from lat" [a lat] (cond (empty? lat) '() (= (first lat) a) (rest lat) :else (cons (first lat) (rember a (rest lat)))))’’’ if __name__ == '__main__':     print rember(2, range(4))     help(rember) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
25 IMPLEMENTATION def clojure(fn):     """Decorator that substitutes an empty python function with clojure in the doc with a callable which delegates to the clojure function. """     clj_namespace = determine_clojure_namespace(fn)     clojure_fnc = build_clojure_function_object(clj_namespace, fn)     fn.__doc__ = get_docs(clojure_fnc)     def aux(*args):         return clojure_fnc.invoke(*args)     functools.update_wrapper(aux, fn)     return aux ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
26 IMPLEMENTATION (2) def determine_clojure_namespace(fn):     try:         clj_namespace = fn.__module__     except AttributeError:         clj_namespace = 'user'     return clj_namespace def get_docs(clojure_fnc):     meta = clojure_fnc.meta()     return meta.get(Keyword.intern('doc')) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
27 IMPLEMENTATION (3) def build_clojure_function_object(clj_namespace, fn):     clojure_code = '(ns %s)n%s' % (         clj_namespace,         fn.__doc__)     clojure_compile_string(clojure_code)     clojure_fnc = RT.var(clj_namespace, fn.func_name)     return clojure_fnc ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
28 CALLING JYTHON FROM CLOJURE (defn make-factory [modulename klassname]   (let [interpreter (PythonInterpreter.) class SpamAndEggs(object):          import-command (str-join " " ["from" modulename     def __init__(self, eggs): "import" klassname])]         self.eggs = eggs     (.exec interpreter import-command)     (.get interpreter klassname))) hasSpam(self):     def         return True (def spam-and-eggs (make-factory "example" "SpamAndEggs"))     def getEggs(self): (def inst (.__call__ spam-and-eggs (Py/java2py 1)))         return self.eggs (println inst)     def __repr__(self): (println (.invoke inst "hasSpam"))         return 'Spam and %s eggs.' % self.eggs
29 CALLING JYTHON FROM CLOJURE (PT. 2) (defn make-factory   [module klassname]   (let [interpreter (PythonInterpreter.)         import-command (str-join " " ["from" module "import" klassname])]     (.exec interpreter import-command)     (let [klass (.get interpreter klassname)]       (fn [& args]         (.__call__ klass           (into-array PyObject             (map #(Py/java2py %) args))))))) (def spam-and-eggs (make-factory "example" "SpamAndEggs")) (def inst (spam-and-eggs 1))
30 THROW MACROS IN (defmacro pyclass [q-class jtype]   (let [[klass-name module-name] (split-module-and-class q-class)]     `(def ~(symbol klass-name)       (make-factory ~(str module-name) ~(str klass-name) ~jtype)))) (pyclass example.PrintSomething java.awt.event.ActionListener) (def evt-printer (PrintSomething)) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
31 MAKE-FACTORY (AGAIN) (defn make-factory [module klassname interface] (let [interpreter (PythonInterpreter.) import-command (str-join " " ["from" module "import" klassname])] (.exec interpreter import-command) (let [klass (.get interpreter klassname)] (fn [& args] (.__tojava__ (.__call__ klass (into-array PyObject (map #(Py/java2py %) args))) interface))))) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
32 BACK TO THE BEGINNING (defn build-gui [title]   (let [frame (JFrame. title)         button (JButton. "CLICK")]     (.addActionListener button (PrintSomething))     (.. frame getContentPane (add button))     (.pack frame)     (.setVisible frame true)     frame)) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
33 CONCLUSION Thanks for Your Kind Attention https://github.com/rik0/PyCLJ_Examples https://github.com/rik0/pyclj ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
INTEROPERABILITY CLOJURE PYTHON Enrico Franchi
35 OUTLINE Why Clojure? Why Java? Clojure from 3000 m. Jython-Clojure interoperability Clojure-Jython interoperability ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
36 GENERAL NOTES During this presentation some very explicit Java code may be shown. No ducks were harmed during the making of this presentation. ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
37 GENERAL NOTES During this presentation some very explicit Java code may be shown. No ducks were harmed during the making of this presentation. ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
38 JVM LANGUAGES Jython Java ? JVM ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
39 JVM LANGUAGES Jython Java Clojure JVM ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
40 CLOJURE Clojure is Lisp Clojure is a good Lisp Clojure has a more functional flavor than Common Lisp; stateful programming is banned unless in very controlled ways Clojure lives on the JVM and perhaps its rejection of state is a reaction to the heavily stateful model of JVM Clojure is not a pure functional language by any means ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
41 JVM LANGUAGES Jython Implementation of Python in Java Jython calls Java ~ Clojure New Programming Languages built on the JVM Design choices in Clojure reflect design choices of the JVM Interoperability ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
42 CLOJURE ~ Good Parts ~ Functional Programming Laziness Full Macros Multi-Methods Immutable types, STM & Concurrency Model ~ Bad Parts ~ “Java” ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
43 ATOMIC TYPES Integers Floating Point Numbers Ratios (3/4 is not 0.75, is ¾) BigDecimal Strings (“foo”) Booleans (true, false) Nil (nil) Characters (a, b) :keywords regexp’s (#“foo.*”) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
44 SEQUENCE TYPES Python Common Lisp Clojure Type Syntax Type Syntax Type Syntax Random access list [1, 2] vector #(1 2) vector [1 2] sequence Linked List - No list (1 2) list (1 2) Set set {1, 2} No No set #{1 2} Map dict {1:2, 3:4} hash-table No vector {1 2, 3 4} Clojure collections In Clojure all collections are immutable; implement corresponding all the functions return a new collections – as usual, Java collections interfaces immutability allows easy data sharing – vector, list List set Set map Map ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
45 CLOJURE EXAMPLE (defn rember [a lat]   (cond    (empty? lat) '()    (= (first lat) a) (rest lat)    :else (cons           (first lat)           (rember a (rest lat))))) (rember 4 '(1 2 3 4 5 6 4)) ; => (1 2 3 5 6 4) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
46 TAIL CALL OPTIMIZATION (defn multirember [a lat]   (letfn       [(multirember-aux [source sink]                    (if (seq source)                      (if (= (first source) a)                        (recur (rest source) sink)                        (recur (rest source)                               (conj sink (first source))))                      sink))]     (multirember-aux lat []))) (take 10 (multirember 4 '(1 2 3 4 5 6 4)) (multirember 4 (iterate inc 0))) ; => [1 2 3 5 6] ; Evaluation aborted. ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
47 LAZINESS (defn lazy-multirember [a lat]   (letfn       [(multirember-aux [source]                         (lazy-seq                          (if (seq source)                            (if (= (first source) a)                              (multirember-aux (rest source))                              (cons (first source)                                    (multirember-aux (rest source))))                           '())))]     (multirember-aux lat))) (take 10 (lazy-multirember 4 (iterate inc 0))) ; => (0 1 2 3 5 6 7 8 9 10)
48 NAMESPACES & MODULES (ns example.namespace) (ns example.namespace (:require clojure.set)) Introduces a new namespace. With def (and defn, …) stuff is (ns example.namespace added to namespaces. (:require [clojure.set :as s])) (ns example.namespace (ns example.namespace (:use clojure.string)) (:import [java.util HashMap])) (ns example.namespace (:use [clojure.string :only [capitalize]])) (ns example.namespace (:use [clojure.string :exclude [capitalize]]) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
49 CALLING JAVA Java Clojure Jython import app.model.Person; (import [app.model Person]) from app.model import Person new Person() (new Person) Person() (Person.) person.getFullName() (. getFullName person) person.getFullName() (.getFullName person) Locale.JAPAN (. Locale JAPAN) Locale.JAPAN Locale/JAPAN Integer.valueOf(“42”) (. Integer valueOf "42") java.lang.Integer.valueOf('42') (Integer/valueOf “42”) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
50 PROXIES (ns gui-sample   (:import [javax.swing JFrame JButton] (gui-sample/build-gui            [java.awt.event ActionListener])) "Rock & Roll" (defn build-gui [title message] "Hello, world!”)   (let [frame (JFrame. title)         button (JButton. "CLICK")]     (.addActionListener button                         (proxy [ActionListener] []                           (actionPerformed [evt]                                            (println message))))     (.. frame getContentPane (add button))     (.pack frame)     (.setVisible frame true)     frame)) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
51 PROXIES (ns gui-sample   (:import [javax.swing JFrame JButton]            [java.awt.event ActionListener])) (defn build-gui [title message]   (let [frame (JFrame. title)         button (JButton. "CLICK")         px (proxy [ActionListener] []                           (actionPerformed [evt]                                            (println message)))]     (.addActionListener button px)     (.. frame getContentPane (add button))     (.pack frame)     (.setVisible frame true) px))      (update-proxy p {"actionPerformed" (fn [this evt] (println "foo!"))}) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
52 GEN-CLASS (ns example.ClassExample   (:gen-class    :name example.ClassExample    :extends Object    :implements []    :methods [ [foo [java.util.Collection] int]              ^{:static true} [show [java.util.Collection] void]])   (:import (java.util Collection))) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
53 GEN-CLASS (2) (defn show [coll] (defn -show [coll]   (if (seq coll)   (show coll))     (do       (print (first coll)) (defn -foo [this coll]       (recur (rest coll)))   (let [coll (seq coll)]     (println "")))     (if coll (count coll) -1))) >>> import example >>> example.ClassExample.show([1, 2, 3]) 123 >>> ce = example.ClassExample() >>> ce.foo([1, 2, 3]) 3
54 IMPLEMENTING PYOBJECTS IN CLOJURE (ns clj.ImmutableList (:gen-class :name clj.ImmutableList :extends org.python.core.PyObject :implements [clojure.lang.IPersistentList] :state state :init init :constructors {[java.util.Collection], []}) (:import [org.python.core PyObject PyInteger Py])) (defn -init [coll] [[coll] (apply list coll)]) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
55 IMPLEMENTING PYOBJECTS IN CLOJURE import clj (ns clj.ImmutableList (:gen-class py_list = ['a', 'b', 'c'] :name clj.ImmutableList my_list = clj.ImmutableList(py_list) :extends org.python.core.PyObject :implements [clojure.lang.IPersistentList] :state state :init init :constructors {[java.util.Collection], []}) (:import [org.python.core PyObject PyInteger Py])) (defn -init [coll] [[coll] (apply list coll)]) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
56 IMPLEMENTING PYOBJECTS IN CLOJURE (2) (defmacro delegate-to-state [sym] (defn -cons [this other] `(defn ~(symbol (str "-" sym)) (cons (.state this) other)) [this#] (~sym (.state this#)))) (defn -equiv [this other] (= (.state this) other)) (delegate-to-state peek) (delegate-to-state pop) (delegate-to-state count) (delegate-to-state empty) (delegate-to-state seq) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
57 IMPLEMENTING PYOBJECTS IN CLOJURE (2) (defmacro delegate-to-state [sym] (defn -cons [this other] `(defn ~(symbol (str "-" sym)) (cons (.state this) other)) [this#] (~sym (.state this#)))) (defn -equiv [this other] print(.state this) other)) (= my_list.peek() (delegate-to-state peek) print my_list.pop() (delegate-to-state pop) print my_list.count() (delegate-to-state count) print my_list.empty() (delegate-to-state empty) print my_list.seq() (delegate-to-state seq) print my_list.cons('d') print my_list.equiv(py_list) print my_list.equiv(['a', 'b', 'c']) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
58 IMPLEMENTING PYOBJECTS print my_list[0] IN CLOJURE (3) print my_list[1] print my_list[2] (defn -__finditem__ [this index] print my_list[2.4] (let [index (if (instance? Number index) index try: (Py/tojava index Number))] print my_list[3] except IndexError, e: (try print e (Py/java2py (nth (.state this) index)) try: (catch IndexOutOfBoundsException e print my_list['a'] (throw (Py/IndexError (.toString e))))))) except TypeError, e: print e try: my_list[0] = 1 except TypeError, e: print e ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
59 CLOJURE RT from clojure.lang import RT, Compiler source = ''' (ns rember) Compiler.load(java.io.StringReader(source)) (defn rember [a lat] rember = RT.var('rember', 'rember') (cond print rember.invoke(2, range(4)) (empty? lat) '() (= (first lat) a) (rest lat) :else (cons (first lat) (rember a (rest lat))))) ''' ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
60 CLOJURE DECORATOR import pyclj @pyclj.clojure def rember(a, lat):     ’’’(defn rember "Remove first occurrence of a from lat" [a lat] (cond (empty? lat) '() (= (first lat) a) (rest lat) :else (cons (first lat) (rember a (rest lat)))))’’’ if __name__ == '__main__':     print rember(2, range(4))     help(rember) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
61 IMPLEMENTATION def clojure(fn):     """Decorator that substitutes an empty python function with clojure in the doc with a callable which delegates to the clojure function. """     clj_namespace = determine_clojure_namespace(fn)     clojure_fnc = build_clojure_function_object(clj_namespace, fn)     fn.__doc__ = get_docs(clojure_fnc)     def aux(*args):         return clojure_fnc.invoke(*args)     functools.update_wrapper(aux, fn)     return aux ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
62 IMPLEMENTATION (2) def determine_clojure_namespace(fn):     try:         clj_namespace = fn.__module__     except AttributeError:         clj_namespace = 'user'     return clj_namespace def get_docs(clojure_fnc):     meta = clojure_fnc.meta()     return meta.get(Keyword.intern('doc')) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
63 IMPLEMENTATION (3) def build_clojure_function_object(clj_namespace, fn):     clojure_code = '(ns %s)n%s' % (         clj_namespace,         fn.__doc__)     clojure_compile_string(clojure_code)     clojure_fnc = RT.var(clj_namespace, fn.func_name)     return clojure_fnc ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
64 CALLING JYTHON FROM CLOJURE (defn make-factory [modulename klassname]   (let [interpreter (PythonInterpreter.)          import-command (str-join " " ["from" modulename "import" klassname])]     (.exec interpreter import-command)     (.get interpreter klassname))) (def spam-and-eggs (make-factory "example" "SpamAndEggs")) (def inst (.__call__ spam-and-eggs (Py/java2py 1))) (println inst) (println (.invoke inst "hasSpam"))
65 CALLING JYTHON FROM CLOJURE (defn make-factory [modulename klassname]   (let [interpreter (PythonInterpreter.) class SpamAndEggs(object):          import-command (str-join " " ["from" modulename     def __init__(self, eggs): "import" klassname])]         self.eggs = eggs     (.exec interpreter import-command)     (.get interpreter klassname))) hasSpam(self):     def         return True (def spam-and-eggs (make-factory "example" "SpamAndEggs"))     def getEggs(self): (def inst (.__call__ spam-and-eggs (Py/java2py 1)))         return self.eggs (println inst)     def __repr__(self): (println (.invoke inst "hasSpam"))         return 'Spam and %s eggs.' % self.eggs
66 CALLING JYTHON FROM CLOJURE (PT. 2) (defn make-factory   [module klassname]   (let [interpreter (PythonInterpreter.)         import-command (str-join " " ["from" module "import" klassname])]     (.exec interpreter import-command)     (let [klass (.get interpreter klassname)]       (fn [& args]         (.__call__ klass           (into-array PyObject             (map #(Py/java2py %) args))))))) (def spam-and-eggs (make-factory "example" "SpamAndEggs")) (def inst (spam-and-eggs 1))
67 THROW MACROS IN (defmacro pyclass [q-class jtype]   (let [[klass-name module-name] (split-module-and-class q-class)]     `(def ~(symbol klass-name)       (make-factory ~(str module-name) ~(str klass-name) ~jtype)))) (pyclass example.PrintSomething java.awt.event.ActionListener) (def evt-printer (PrintSomething)) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
68 MAKE-FACTORY (AGAIN) (defn make-factory [module klassname interface] (let [interpreter (PythonInterpreter.) import-command (str-join " " ["from" module "import" klassname])] (.exec interpreter import-command) (let [klass (.get interpreter klassname)] (fn [& args] (.__tojava__ (.__call__ klass (into-array PyObject (map #(Py/java2py %) args))) interface))))) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
69 BACK TO THE BEGINNING (defn build-gui [title]   (let [frame (JFrame. title)         button (JButton. "CLICK")]     (.addActionListener button (PrintSomething))     (.. frame getContentPane (add button))     (.pack frame)     (.setVisible frame true)     frame)) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
70 CONCLUSION Thanks for Your Kind Attention https://github.com/rik0/PyCLJ_Examples https://github.com/rik0/pyclj ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>

Clojure Interoperability

  • 1.
    INTEROPERABILITY CLOJURE PYTHON Enrico Franchi
  • 2.
    2 OUTLINE Why Clojure? Why Java? Clojure from 3000 m. Jython-Clojure interoperability Clojure-Jython interoperability ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 3.
    3 GENERAL NOTES During this presentation some very explicit Java code may be shown. No ducks were harmed during the making of this presentation. ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 4.
    4 JVM LANGUAGES Jython Java ? JVM ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 5.
    5 JVM LANGUAGES Jython Java Clojure JVM ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 6.
    6 CLOJURE Clojure is Lisp Clojure is a good Lisp Clojure has a more functional flavor than Common Lisp; stateful programming is banned unless in very controlled ways Clojure lives on the JVM and perhaps its rejection of state is a reaction to the heavily stateful model of JVM Clojure is not a pure functional language by any means ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 7.
    7 JVM LANGUAGES Jython Implementation of Python in Java Jython calls Java ~ Clojure New Programming Languages built on the JVM Design choices in Clojure reflect design choices of the JVM Interoperability ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 8.
    8 CLOJURE ~ Good Parts ~ Functional Programming Laziness Full Macros Multi-Methods Immutable types, STM & Concurrency Model ~ Bad Parts ~ “Java” ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 9.
    9 ATOMIC TYPES Integers Floating Point Numbers Ratios (3/4 is not 0.75, is ¾) BigDecimal Strings (“foo”) Booleans (true, false) Nil (nil) Characters (a, b) :keywords regexp’s (#“foo.*”) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 10.
    10 SEQUENCE TYPES Python Common Lisp Clojure Type Syntax Type Syntax Type Syntax Random access list [1, 2] vector #(1 2) vector [1 2] sequence Linked List - No list (1 2) list (1 2) Set set {1, 2} No No set #{1 2} Map dict {1:2, 3:4} hash-table No vector {1 2, 3 4} Clojure collections In Clojure all collections are immutable; implement corresponding all the functions return a new collections – as usual, Java collections interfaces immutability allows easy data sharing – vector, list List set Set map Map ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 11.
    11 CLOJURE EXAMPLE (defn rember [a lat]   (cond    (empty? lat) '()    (= (first lat) a) (rest lat)    :else (cons           (first lat)           (rember a (rest lat))))) (rember 4 '(1 2 3 4 5 6 4)) ; => (1 2 3 5 6 4) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 12.
    12 TAIL CALL OPTIMIZATION (defn multirember [a lat]   (letfn       [(multirember-aux [source sink]                    (if (seq source)                      (if (= (first source) a)                        (recur (rest source) sink)                        (recur (rest source)                               (conj sink (first source))))                      sink))]     (multirember-aux lat []))) (take 10 (multirember 4 '(1 2 3 4 5 6 4)) (multirember 4 (iterate inc 0))) ; => [1 2 3 5 6] ; Evaluation aborted. ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 13.
    13 LAZINESS (defn lazy-multirember [a lat]   (letfn       [(multirember-aux [source]                         (lazy-seq                          (if (seq source)                            (if (= (first source) a)                              (multirember-aux (rest source))                              (cons (first source)                                    (multirember-aux (rest source))))                           '())))]     (multirember-aux lat))) (take 10 (lazy-multirember 4 (iterate inc 0))) ; => (0 1 2 3 5 6 7 8 9 10)
  • 14.
    14 NAMESPACES & MODULES (ns example.namespace) (ns example.namespace (:require clojure.set)) Introduces a new namespace. With def (and defn, …) stuff is (ns example.namespace added to namespaces. (:require [clojure.set :as s])) (ns example.namespace (ns example.namespace (:use clojure.string)) (:import [java.util HashMap])) (ns example.namespace (:use [clojure.string :only [capitalize]])) (ns example.namespace (:use [clojure.string :exclude [capitalize]]) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 15.
    15 CALLING JAVA Java Clojure Jython import app.model.Person; (import [app.model Person]) from app.model import Person new Person() (new Person) Person() (Person.) person.getFullName() (. getFullName person) person.getFullName() (.getFullName person) Locale.JAPAN (. Locale JAPAN) Locale.JAPAN Locale/JAPAN Integer.valueOf(“42”) (. Integer valueOf "42") java.lang.Integer.valueOf('42') (Integer/valueOf “42”) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 16.
    16 PROXIES (ns gui-sample   (:import [javax.swing JFrame JButton] (gui-sample/build-gui            [java.awt.event ActionListener])) "Rock & Roll" (defn build-gui [title message] "Hello, world!”)   (let [frame (JFrame. title)         button (JButton. "CLICK")]     (.addActionListener button                         (proxy [ActionListener] []                           (actionPerformed [evt]                                            (println message))))     (.. frame getContentPane (add button))     (.pack frame)     (.setVisible frame true)     frame)) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 17.
    17 PROXIES (ns gui-sample   (:import [javax.swing JFrame JButton]            [java.awt.event ActionListener])) (defn build-gui [title message]   (let [frame (JFrame. title)         button (JButton. "CLICK")         px (proxy [ActionListener] []                           (actionPerformed [evt]                                            (println message)))]     (.addActionListener button px)     (.. frame getContentPane (add button))     (.pack frame)     (.setVisible frame true) px))      (update-proxy p {"actionPerformed" (fn [this evt] (println "foo!"))}) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 18.
    18 GEN-CLASS (ns example.ClassExample   (:gen-class    :name example.ClassExample    :extends Object    :implements []    :methods [ [foo [java.util.Collection] int]              ^{:static true} [show [java.util.Collection] void]])   (:import (java.util Collection))) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 19.
    19 GEN-CLASS (2) (defn show [coll] (defn -show [coll]   (if (seq coll)   (show coll))     (do       (print (first coll)) (defn -foo [this coll]       (recur (rest coll)))   (let [coll (seq coll)]     (println "")))     (if coll (count coll) -1))) >>> import example >>> example.ClassExample.show([1, 2, 3]) 123 >>> ce = example.ClassExample() >>> ce.foo([1, 2, 3]) 3
  • 20.
    20 IMPLEMENTING PYOBJECTS IN CLOJURE import clj (ns clj.ImmutableList (:gen-class py_list = ['a', 'b', 'c'] :name clj.ImmutableList my_list = clj.ImmutableList(py_list) :extends org.python.core.PyObject :implements [clojure.lang.IPersistentList] :state state :init init :constructors {[java.util.Collection], []}) (:import [org.python.core PyObject PyInteger Py])) (defn -init [coll] [[coll] (apply list coll)]) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 21.
    21 IMPLEMENTING PYOBJECTS IN CLOJURE (2) (defmacro delegate-to-state [sym] (defn -cons [this other] `(defn ~(symbol (str "-" sym)) (cons (.state this) other)) [this#] (~sym (.state this#)))) (defn -equiv [this other] print(.state this) other)) (= my_list.peek() (delegate-to-state peek) print my_list.pop() (delegate-to-state pop) print my_list.count() (delegate-to-state count) print my_list.empty() (delegate-to-state empty) print my_list.seq() (delegate-to-state seq) print my_list.cons('d') print my_list.equiv(py_list) print my_list.equiv(['a', 'b', 'c']) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 22.
    22 IMPLEMENTING PYOBJECTS print my_list[0] IN CLOJURE (3) print my_list[1] print my_list[2] (defn -__finditem__ [this index] print my_list[2.4] (let [index (if (instance? Number index) index try: (Py/tojava index Number))] print my_list[3] except IndexError, e: (try print e (Py/java2py (nth (.state this) index)) try: (catch IndexOutOfBoundsException e print my_list['a'] (throw (Py/IndexError (.toString e))))))) except TypeError, e: print e try: my_list[0] = 1 except TypeError, e: print e ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 23.
    23 CLOJURE RT from clojure.lang import RT, Compiler source = ''' (ns rember) Compiler.load(java.io.StringReader(source)) (defn rember [a lat] rember = RT.var('rember', 'rember') (cond print rember.invoke(2, range(4)) (empty? lat) '() (= (first lat) a) (rest lat) :else (cons (first lat) (rember a (rest lat))))) ''' ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 24.
    24 CLOJURE DECORATOR import pyclj @pyclj.clojure def rember(a, lat):     ’’’(defn rember "Remove first occurrence of a from lat" [a lat] (cond (empty? lat) '() (= (first lat) a) (rest lat) :else (cons (first lat) (rember a (rest lat)))))’’’ if __name__ == '__main__':     print rember(2, range(4))     help(rember) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 25.
    25 IMPLEMENTATION def clojure(fn):     """Decorator that substitutes an empty python function with clojure in the doc with a callable which delegates to the clojure function. """     clj_namespace = determine_clojure_namespace(fn)     clojure_fnc = build_clojure_function_object(clj_namespace, fn)     fn.__doc__ = get_docs(clojure_fnc)     def aux(*args):         return clojure_fnc.invoke(*args)     functools.update_wrapper(aux, fn)     return aux ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 26.
    26 IMPLEMENTATION (2) def determine_clojure_namespace(fn):     try:         clj_namespace = fn.__module__     except AttributeError:         clj_namespace = 'user'     return clj_namespace def get_docs(clojure_fnc):     meta = clojure_fnc.meta()     return meta.get(Keyword.intern('doc')) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 27.
    27 IMPLEMENTATION (3) def build_clojure_function_object(clj_namespace, fn):     clojure_code = '(ns %s)n%s' % (         clj_namespace,         fn.__doc__)     clojure_compile_string(clojure_code)     clojure_fnc = RT.var(clj_namespace, fn.func_name)     return clojure_fnc ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 28.
    28 CALLING JYTHON FROM CLOJURE (defn make-factory [modulename klassname]   (let [interpreter (PythonInterpreter.) class SpamAndEggs(object):          import-command (str-join " " ["from" modulename     def __init__(self, eggs): "import" klassname])]         self.eggs = eggs     (.exec interpreter import-command)     (.get interpreter klassname))) hasSpam(self):     def         return True (def spam-and-eggs (make-factory "example" "SpamAndEggs"))     def getEggs(self): (def inst (.__call__ spam-and-eggs (Py/java2py 1)))         return self.eggs (println inst)     def __repr__(self): (println (.invoke inst "hasSpam"))         return 'Spam and %s eggs.' % self.eggs
  • 29.
    29 CALLING JYTHON FROM CLOJURE (PT. 2) (defn make-factory   [module klassname]   (let [interpreter (PythonInterpreter.)         import-command (str-join " " ["from" module "import" klassname])]     (.exec interpreter import-command)     (let [klass (.get interpreter klassname)]       (fn [& args]         (.__call__ klass           (into-array PyObject             (map #(Py/java2py %) args))))))) (def spam-and-eggs (make-factory "example" "SpamAndEggs")) (def inst (spam-and-eggs 1))
  • 30.
    30 THROW MACROS IN (defmacro pyclass [q-class jtype]   (let [[klass-name module-name] (split-module-and-class q-class)]     `(def ~(symbol klass-name)       (make-factory ~(str module-name) ~(str klass-name) ~jtype)))) (pyclass example.PrintSomething java.awt.event.ActionListener) (def evt-printer (PrintSomething)) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 31.
    31 MAKE-FACTORY (AGAIN) (defn make-factory [module klassname interface] (let [interpreter (PythonInterpreter.) import-command (str-join " " ["from" module "import" klassname])] (.exec interpreter import-command) (let [klass (.get interpreter klassname)] (fn [& args] (.__tojava__ (.__call__ klass (into-array PyObject (map #(Py/java2py %) args))) interface))))) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 32.
    32 BACK TO THE BEGINNING (defn build-gui [title]   (let [frame (JFrame. title)         button (JButton. "CLICK")]     (.addActionListener button (PrintSomething))     (.. frame getContentPane (add button))     (.pack frame)     (.setVisible frame true)     frame)) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 33.
    33 CONCLUSION Thanks for Your Kind Attention https://github.com/rik0/PyCLJ_Examples https://github.com/rik0/pyclj ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 34.
    INTEROPERABILITY CLOJURE PYTHON Enrico Franchi
  • 35.
    35 OUTLINE Why Clojure? Why Java? Clojure from 3000 m. Jython-Clojure interoperability Clojure-Jython interoperability ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 36.
    36 GENERAL NOTES During this presentation some very explicit Java code may be shown. No ducks were harmed during the making of this presentation. ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 37.
    37 GENERAL NOTES During this presentation some very explicit Java code may be shown. No ducks were harmed during the making of this presentation. ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 38.
    38 JVM LANGUAGES Jython Java ? JVM ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 39.
    39 JVM LANGUAGES Jython Java Clojure JVM ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 40.
    40 CLOJURE Clojure is Lisp Clojure is a good Lisp Clojure has a more functional flavor than Common Lisp; stateful programming is banned unless in very controlled ways Clojure lives on the JVM and perhaps its rejection of state is a reaction to the heavily stateful model of JVM Clojure is not a pure functional language by any means ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 41.
    41 JVM LANGUAGES Jython Implementation of Python in Java Jython calls Java ~ Clojure New Programming Languages built on the JVM Design choices in Clojure reflect design choices of the JVM Interoperability ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 42.
    42 CLOJURE ~ Good Parts ~ Functional Programming Laziness Full Macros Multi-Methods Immutable types, STM & Concurrency Model ~ Bad Parts ~ “Java” ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 43.
    43 ATOMIC TYPES Integers Floating Point Numbers Ratios (3/4 is not 0.75, is ¾) BigDecimal Strings (“foo”) Booleans (true, false) Nil (nil) Characters (a, b) :keywords regexp’s (#“foo.*”) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 44.
    44 SEQUENCE TYPES Python Common Lisp Clojure Type Syntax Type Syntax Type Syntax Random access list [1, 2] vector #(1 2) vector [1 2] sequence Linked List - No list (1 2) list (1 2) Set set {1, 2} No No set #{1 2} Map dict {1:2, 3:4} hash-table No vector {1 2, 3 4} Clojure collections In Clojure all collections are immutable; implement corresponding all the functions return a new collections – as usual, Java collections interfaces immutability allows easy data sharing – vector, list List set Set map Map ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 45.
    45 CLOJURE EXAMPLE (defn rember [a lat]   (cond    (empty? lat) '()    (= (first lat) a) (rest lat)    :else (cons           (first lat)           (rember a (rest lat))))) (rember 4 '(1 2 3 4 5 6 4)) ; => (1 2 3 5 6 4) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 46.
    46 TAIL CALL OPTIMIZATION (defn multirember [a lat]   (letfn       [(multirember-aux [source sink]                    (if (seq source)                      (if (= (first source) a)                        (recur (rest source) sink)                        (recur (rest source)                               (conj sink (first source))))                      sink))]     (multirember-aux lat []))) (take 10 (multirember 4 '(1 2 3 4 5 6 4)) (multirember 4 (iterate inc 0))) ; => [1 2 3 5 6] ; Evaluation aborted. ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 47.
    47 LAZINESS (defn lazy-multirember [a lat]   (letfn       [(multirember-aux [source]                         (lazy-seq                          (if (seq source)                            (if (= (first source) a)                              (multirember-aux (rest source))                              (cons (first source)                                    (multirember-aux (rest source))))                           '())))]     (multirember-aux lat))) (take 10 (lazy-multirember 4 (iterate inc 0))) ; => (0 1 2 3 5 6 7 8 9 10)
  • 48.
    48 NAMESPACES & MODULES (ns example.namespace) (ns example.namespace (:require clojure.set)) Introduces a new namespace. With def (and defn, …) stuff is (ns example.namespace added to namespaces. (:require [clojure.set :as s])) (ns example.namespace (ns example.namespace (:use clojure.string)) (:import [java.util HashMap])) (ns example.namespace (:use [clojure.string :only [capitalize]])) (ns example.namespace (:use [clojure.string :exclude [capitalize]]) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 49.
    49 CALLING JAVA Java Clojure Jython import app.model.Person; (import [app.model Person]) from app.model import Person new Person() (new Person) Person() (Person.) person.getFullName() (. getFullName person) person.getFullName() (.getFullName person) Locale.JAPAN (. Locale JAPAN) Locale.JAPAN Locale/JAPAN Integer.valueOf(“42”) (. Integer valueOf "42") java.lang.Integer.valueOf('42') (Integer/valueOf “42”) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 50.
    50 PROXIES (ns gui-sample   (:import [javax.swing JFrame JButton] (gui-sample/build-gui            [java.awt.event ActionListener])) "Rock & Roll" (defn build-gui [title message] "Hello, world!”)   (let [frame (JFrame. title)         button (JButton. "CLICK")]     (.addActionListener button                         (proxy [ActionListener] []                           (actionPerformed [evt]                                            (println message))))     (.. frame getContentPane (add button))     (.pack frame)     (.setVisible frame true)     frame)) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 51.
    51 PROXIES (ns gui-sample   (:import [javax.swing JFrame JButton]            [java.awt.event ActionListener])) (defn build-gui [title message]   (let [frame (JFrame. title)         button (JButton. "CLICK")         px (proxy [ActionListener] []                           (actionPerformed [evt]                                            (println message)))]     (.addActionListener button px)     (.. frame getContentPane (add button))     (.pack frame)     (.setVisible frame true) px))      (update-proxy p {"actionPerformed" (fn [this evt] (println "foo!"))}) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 52.
    52 GEN-CLASS (ns example.ClassExample   (:gen-class    :name example.ClassExample    :extends Object    :implements []    :methods [ [foo [java.util.Collection] int]              ^{:static true} [show [java.util.Collection] void]])   (:import (java.util Collection))) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 53.
    53 GEN-CLASS (2) (defn show [coll] (defn -show [coll]   (if (seq coll)   (show coll))     (do       (print (first coll)) (defn -foo [this coll]       (recur (rest coll)))   (let [coll (seq coll)]     (println "")))     (if coll (count coll) -1))) >>> import example >>> example.ClassExample.show([1, 2, 3]) 123 >>> ce = example.ClassExample() >>> ce.foo([1, 2, 3]) 3
  • 54.
    54 IMPLEMENTING PYOBJECTS IN CLOJURE (ns clj.ImmutableList (:gen-class :name clj.ImmutableList :extends org.python.core.PyObject :implements [clojure.lang.IPersistentList] :state state :init init :constructors {[java.util.Collection], []}) (:import [org.python.core PyObject PyInteger Py])) (defn -init [coll] [[coll] (apply list coll)]) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 55.
    55 IMPLEMENTING PYOBJECTS IN CLOJURE import clj (ns clj.ImmutableList (:gen-class py_list = ['a', 'b', 'c'] :name clj.ImmutableList my_list = clj.ImmutableList(py_list) :extends org.python.core.PyObject :implements [clojure.lang.IPersistentList] :state state :init init :constructors {[java.util.Collection], []}) (:import [org.python.core PyObject PyInteger Py])) (defn -init [coll] [[coll] (apply list coll)]) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 56.
    56 IMPLEMENTING PYOBJECTS IN CLOJURE (2) (defmacro delegate-to-state [sym] (defn -cons [this other] `(defn ~(symbol (str "-" sym)) (cons (.state this) other)) [this#] (~sym (.state this#)))) (defn -equiv [this other] (= (.state this) other)) (delegate-to-state peek) (delegate-to-state pop) (delegate-to-state count) (delegate-to-state empty) (delegate-to-state seq) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 57.
    57 IMPLEMENTING PYOBJECTS IN CLOJURE (2) (defmacro delegate-to-state [sym] (defn -cons [this other] `(defn ~(symbol (str "-" sym)) (cons (.state this) other)) [this#] (~sym (.state this#)))) (defn -equiv [this other] print(.state this) other)) (= my_list.peek() (delegate-to-state peek) print my_list.pop() (delegate-to-state pop) print my_list.count() (delegate-to-state count) print my_list.empty() (delegate-to-state empty) print my_list.seq() (delegate-to-state seq) print my_list.cons('d') print my_list.equiv(py_list) print my_list.equiv(['a', 'b', 'c']) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 58.
    58 IMPLEMENTING PYOBJECTS print my_list[0] IN CLOJURE (3) print my_list[1] print my_list[2] (defn -__finditem__ [this index] print my_list[2.4] (let [index (if (instance? Number index) index try: (Py/tojava index Number))] print my_list[3] except IndexError, e: (try print e (Py/java2py (nth (.state this) index)) try: (catch IndexOutOfBoundsException e print my_list['a'] (throw (Py/IndexError (.toString e))))))) except TypeError, e: print e try: my_list[0] = 1 except TypeError, e: print e ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 59.
    59 CLOJURE RT from clojure.lang import RT, Compiler source = ''' (ns rember) Compiler.load(java.io.StringReader(source)) (defn rember [a lat] rember = RT.var('rember', 'rember') (cond print rember.invoke(2, range(4)) (empty? lat) '() (= (first lat) a) (rest lat) :else (cons (first lat) (rember a (rest lat))))) ''' ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 60.
    60 CLOJURE DECORATOR import pyclj @pyclj.clojure def rember(a, lat):     ’’’(defn rember "Remove first occurrence of a from lat" [a lat] (cond (empty? lat) '() (= (first lat) a) (rest lat) :else (cons (first lat) (rember a (rest lat)))))’’’ if __name__ == '__main__':     print rember(2, range(4))     help(rember) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 61.
    61 IMPLEMENTATION def clojure(fn):     """Decorator that substitutes an empty python function with clojure in the doc with a callable which delegates to the clojure function. """     clj_namespace = determine_clojure_namespace(fn)     clojure_fnc = build_clojure_function_object(clj_namespace, fn)     fn.__doc__ = get_docs(clojure_fnc)     def aux(*args):         return clojure_fnc.invoke(*args)     functools.update_wrapper(aux, fn)     return aux ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 62.
    62 IMPLEMENTATION (2) def determine_clojure_namespace(fn):     try:         clj_namespace = fn.__module__     except AttributeError:         clj_namespace = 'user'     return clj_namespace def get_docs(clojure_fnc):     meta = clojure_fnc.meta()     return meta.get(Keyword.intern('doc')) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 63.
    63 IMPLEMENTATION (3) def build_clojure_function_object(clj_namespace, fn):     clojure_code = '(ns %s)n%s' % (         clj_namespace,         fn.__doc__)     clojure_compile_string(clojure_code)     clojure_fnc = RT.var(clj_namespace, fn.func_name)     return clojure_fnc ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 64.
    64 CALLING JYTHON FROM CLOJURE (defn make-factory [modulename klassname]   (let [interpreter (PythonInterpreter.)          import-command (str-join " " ["from" modulename "import" klassname])]     (.exec interpreter import-command)     (.get interpreter klassname))) (def spam-and-eggs (make-factory "example" "SpamAndEggs")) (def inst (.__call__ spam-and-eggs (Py/java2py 1))) (println inst) (println (.invoke inst "hasSpam"))
  • 65.
    65 CALLING JYTHON FROM CLOJURE (defn make-factory [modulename klassname]   (let [interpreter (PythonInterpreter.) class SpamAndEggs(object):          import-command (str-join " " ["from" modulename     def __init__(self, eggs): "import" klassname])]         self.eggs = eggs     (.exec interpreter import-command)     (.get interpreter klassname))) hasSpam(self):     def         return True (def spam-and-eggs (make-factory "example" "SpamAndEggs"))     def getEggs(self): (def inst (.__call__ spam-and-eggs (Py/java2py 1)))         return self.eggs (println inst)     def __repr__(self): (println (.invoke inst "hasSpam"))         return 'Spam and %s eggs.' % self.eggs
  • 66.
    66 CALLING JYTHON FROM CLOJURE (PT. 2) (defn make-factory   [module klassname]   (let [interpreter (PythonInterpreter.)         import-command (str-join " " ["from" module "import" klassname])]     (.exec interpreter import-command)     (let [klass (.get interpreter klassname)]       (fn [& args]         (.__call__ klass           (into-array PyObject             (map #(Py/java2py %) args))))))) (def spam-and-eggs (make-factory "example" "SpamAndEggs")) (def inst (spam-and-eggs 1))
  • 67.
    67 THROW MACROS IN (defmacro pyclass [q-class jtype]   (let [[klass-name module-name] (split-module-and-class q-class)]     `(def ~(symbol klass-name)       (make-factory ~(str module-name) ~(str klass-name) ~jtype)))) (pyclass example.PrintSomething java.awt.event.ActionListener) (def evt-printer (PrintSomething)) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 68.
    68 MAKE-FACTORY (AGAIN) (defn make-factory [module klassname interface] (let [interpreter (PythonInterpreter.) import-command (str-join " " ["from" module "import" klassname])] (.exec interpreter import-command) (let [klass (.get interpreter klassname)] (fn [& args] (.__tojava__ (.__call__ klass (into-array PyObject (map #(Py/java2py %) args))) interface))))) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 69.
    69 BACK TO THE BEGINNING (defn build-gui [title]   (let [frame (JFrame. title)         button (JButton. "CLICK")]     (.addActionListener button (PrintSomething))     (.. frame getContentPane (add button))     (.pack frame)     (.setVisible frame true)     frame)) ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>
  • 70.
    70 CONCLUSION Thanks for Your Kind Attention https://github.com/rik0/PyCLJ_Examples https://github.com/rik0/pyclj ENRICO FRANCHI <EFRANCHI@CE.UNIPR.IT>