Clojure Interoperability

70
Enrico Franchi INTEROPERABILITY CLOJURE PYTHON

description

Some idea

Transcript of Clojure Interoperability

Page 1: Clojure Interoperability

Enrico Franchi

INTEROPERABILITY CLOJURE ⇄ PYTHON

Page 2: Clojure Interoperability

Why Clojure? Why Java?

Clojure from 3000 m.

Jython-Clojure interoperability

Clojure-Jython interoperability

OUTLINE

2

ENRICO FRANCHI <[email protected]>

Page 3: Clojure Interoperability

During this presentation some very explicit Java code may be shown.

No ducks were harmed during the making of this presentation.

GENERAL NOTES

3

ENRICO FRANCHI <[email protected]>

Page 4: Clojure Interoperability

JVM

Jython Java ?

JVM LANGUAGES

4

ENRICO FRANCHI <[email protected]>

Page 5: Clojure Interoperability

JVM LANGUAGES

JVM

Jython Java Clojure

5

ENRICO FRANCHI <[email protected]>

Page 6: Clojure Interoperability

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

CLOJURE

6

ENRICO FRANCHI <[email protected]>

Page 7: Clojure Interoperability

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

JVM LANGUAGES

7

ENRICO FRANCHI <[email protected]>

Page 8: Clojure Interoperability

~ Good Parts ~

Functional Programming Laziness

Full Macros Multi-Methods

Immutable types, STM & Concurrency Model

~ Bad Parts ~

“Java”

CLOJURE

8

ENRICO FRANCHI <[email protected]>

Page 9: Clojure Interoperability

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.*”)

ATOMIC TYPES

9

ENRICO FRANCHI <[email protected]>

Page 10: Clojure Interoperability

Python Common Lisp Clojure

Type Syntax Type Syntax Type Syntax

Random access sequence

list [1, 2] vector #(1 2) vector [1 2]

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}

SEQUENCE TYPES

In Clojure all collections are immutable; all the functions return a new collections – as usual, immutability allows easy data sharing –

Clojure collections implement corresponding Java collections interfaces

vector, list List

set Set

map Map

10

ENRICO FRANCHI <[email protected]>

Page 11: Clojure Interoperability

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)

11

ENRICO FRANCHI <[email protected]>

Page 12: Clojure Interoperability

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 []))) (multirember 4 '(1 2 3 4 5 6 4)) ; => [1 2 3 5 6]

(take 10 (multirember 4 (iterate inc 0))) ; Evaluation aborted.

12

ENRICO FRANCHI <[email protected]>

Page 13: Clojure Interoperability

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)

13

Page 14: Clojure Interoperability

NAMESPACES & MODULES

(ns example.namespace) Introduces a new namespace. With def (and defn, …) stuff is added to namespaces.

(ns example.namespace (:require clojure.set)) (ns example.namespace (:require [clojure.set :as s]))

(ns example.namespace (:use clojure.string)) (ns example.namespace (:use [clojure.string :only [capitalize]])) (ns example.namespace (:use [clojure.string :exclude [capitalize]])

(ns example.namespace (:import [java.util HashMap]))

14

ENRICO FRANCHI <[email protected]>

Page 15: Clojure Interoperability

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) (.getFullName person)

person.getFullName()

Locale.JAPAN (. Locale JAPAN) Locale/JAPAN

Locale.JAPAN

Integer.valueOf(“42”) (. Integer valueOf "42") (Integer/valueOf “42”)

java.lang.Integer.valueOf('42')

CALLING JAVA

15

ENRICO FRANCHI <[email protected]>

Page 16: Clojure Interoperability

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")]     (.addActionListener button                         (proxy [ActionListener] []                           (actionPerformed [evt]                                            (println message))))     (.. frame getContentPane (add button))     (.pack frame)     (.setVisible frame true)     frame))

(gui-sample/build-gui "Rock & Roll" "Hello, world!”)

16

ENRICO FRANCHI <[email protected]>

Page 17: Clojure Interoperability

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

17

ENRICO FRANCHI <[email protected]>

Page 18: Clojure Interoperability

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

18

ENRICO FRANCHI <[email protected]>

Page 19: Clojure Interoperability

GEN-CLASS (2)

(defn show [coll]   (if (seq coll)     (do       (print (first coll))       (recur (rest coll)))     (println "")))

(defn -show [coll]   (show coll)) (defn -foo [this coll]   (let [coll (seq coll)]     (if coll (count coll) -1)))

>>> import example >>> example.ClassExample.show([1, 2, 3]) 123 >>> ce = example.ClassExample() >>> ce.foo([1, 2, 3]) 3

19

Page 20: Clojure Interoperability

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

import clj py_list = ['a', 'b', 'c'] my_list = clj.ImmutableList(py_list)

20

ENRICO FRANCHI <[email protected]>

Page 21: Clojure Interoperability

IMPLEMENTING PYOBJECTS IN CLOJURE (2)

(defmacro delegate-to-state [sym] `(defn ~(symbol (str "-" sym)) [this#] (~sym (.state this#)))) (delegate-to-state peek) (delegate-to-state pop) (delegate-to-state count) (delegate-to-state empty) (delegate-to-state seq)

(defn -cons [this other] (cons (.state this) other)) (defn -equiv [this other] (= (.state this) other)) print my_list.peek() print my_list.pop() print my_list.count() print my_list.empty() print my_list.seq() print my_list.cons('d') print my_list.equiv(py_list) print my_list.equiv(['a', 'b', 'c'])

21

ENRICO FRANCHI <[email protected]>

Page 22: Clojure Interoperability

IMPLEMENTING PYOBJECTS IN CLOJURE (3)

(defn -__finditem__ [this index] (let [index (if (instance? Number index) index (Py/tojava index Number))] (try (Py/java2py (nth (.state this) index)) (catch IndexOutOfBoundsException e (throw (Py/IndexError (.toString e)))))))

print my_list[0] print my_list[1] print my_list[2] print my_list[2.4] try: print my_list[3] except IndexError, e: print e try: print my_list['a'] except TypeError, e: print e try: my_list[0] = 1 except TypeError, e: print e

22

ENRICO FRANCHI <[email protected]>

Page 23: Clojure Interoperability

CLOJURE RT

source = ''' (ns rember) (defn rember [a lat] (cond (empty? lat) '() (= (first lat) a) (rest lat) :else (cons (first lat) (rember a (rest lat))))) '''

from clojure.lang import RT, Compiler Compiler.load(java.io.StringReader(source)) rember = RT.var('rember', 'rember') print rember.invoke(2, range(4))

23

ENRICO FRANCHI <[email protected]>

Page 24: Clojure Interoperability

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)

24

ENRICO FRANCHI <[email protected]>

Page 25: Clojure Interoperability

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

25

ENRICO FRANCHI <[email protected]>

Page 26: Clojure Interoperability

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

26

ENRICO FRANCHI <[email protected]>

Page 27: Clojure Interoperability

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

27

ENRICO FRANCHI <[email protected]>

Page 28: Clojure Interoperability

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

class SpamAndEggs(object):     def __init__(self, eggs):         self.eggs = eggs     def hasSpam(self):         return True     def getEggs(self):         return self.eggs     def __repr__(self):         return 'Spam and %s eggs.' % self.eggs

28

Page 29: Clojure Interoperability

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

29

Page 30: Clojure Interoperability

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

30

ENRICO FRANCHI <[email protected]>

Page 31: Clojure Interoperability

MAKE-FACTORY (AGAIN)

31

ENRICO FRANCHI <[email protected]>

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

Page 32: Clojure Interoperability

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

32

ENRICO FRANCHI <[email protected]>

Page 33: Clojure Interoperability

Thanks for Your Kind Attention

CONCLUSION

https://github.com/rik0/PyCLJ_Examples https://github.com/rik0/pyclj

33

ENRICO FRANCHI <[email protected]>

Page 34: Clojure Interoperability

Enrico Franchi

INTEROPERABILITY CLOJURE ⇄ PYTHON

Page 35: Clojure Interoperability

Why Clojure? Why Java?

Clojure from 3000 m.

Jython-Clojure interoperability

Clojure-Jython interoperability

OUTLINE

35

ENRICO FRANCHI <[email protected]>

Page 36: Clojure Interoperability

During this presentation some very explicit Java code may be shown.

No ducks were harmed during the making of this presentation.

GENERAL NOTES

36

ENRICO FRANCHI <[email protected]>

Page 37: Clojure Interoperability

During this presentation some very explicit Java code may be shown.

No ducks were harmed during the making of this presentation.

GENERAL NOTES

37

ENRICO FRANCHI <[email protected]>

Page 38: Clojure Interoperability

JVM

Jython Java ?

JVM LANGUAGES

38

ENRICO FRANCHI <[email protected]>

Page 39: Clojure Interoperability

JVM LANGUAGES

JVM

Jython Java Clojure

39

ENRICO FRANCHI <[email protected]>

Page 40: Clojure Interoperability

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

CLOJURE

40

ENRICO FRANCHI <[email protected]>

Page 41: Clojure Interoperability

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

JVM LANGUAGES

41

ENRICO FRANCHI <[email protected]>

Page 42: Clojure Interoperability

~ Good Parts ~

Functional Programming Laziness

Full Macros Multi-Methods

Immutable types, STM & Concurrency Model

~ Bad Parts ~

“Java”

CLOJURE

42

ENRICO FRANCHI <[email protected]>

Page 43: Clojure Interoperability

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.*”)

ATOMIC TYPES

43

ENRICO FRANCHI <[email protected]>

Page 44: Clojure Interoperability

Python Common Lisp Clojure

Type Syntax Type Syntax Type Syntax

Random access sequence

list [1, 2] vector #(1 2) vector [1 2]

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}

SEQUENCE TYPES

In Clojure all collections are immutable; all the functions return a new collections – as usual, immutability allows easy data sharing –

Clojure collections implement corresponding Java collections interfaces

vector, list List

set Set

map Map

44

ENRICO FRANCHI <[email protected]>

Page 45: Clojure Interoperability

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)

45

ENRICO FRANCHI <[email protected]>

Page 46: Clojure Interoperability

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 []))) (multirember 4 '(1 2 3 4 5 6 4)) ; => [1 2 3 5 6]

(take 10 (multirember 4 (iterate inc 0))) ; Evaluation aborted.

46

ENRICO FRANCHI <[email protected]>

Page 47: Clojure Interoperability

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)

47

Page 48: Clojure Interoperability

NAMESPACES & MODULES

(ns example.namespace) Introduces a new namespace. With def (and defn, …) stuff is added to namespaces.

(ns example.namespace (:require clojure.set)) (ns example.namespace (:require [clojure.set :as s]))

(ns example.namespace (:use clojure.string)) (ns example.namespace (:use [clojure.string :only [capitalize]])) (ns example.namespace (:use [clojure.string :exclude [capitalize]])

(ns example.namespace (:import [java.util HashMap]))

48

ENRICO FRANCHI <[email protected]>

Page 49: Clojure Interoperability

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) (.getFullName person)

person.getFullName()

Locale.JAPAN (. Locale JAPAN) Locale/JAPAN

Locale.JAPAN

Integer.valueOf(“42”) (. Integer valueOf "42") (Integer/valueOf “42”)

java.lang.Integer.valueOf('42')

CALLING JAVA

49

ENRICO FRANCHI <[email protected]>

Page 50: Clojure Interoperability

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")]     (.addActionListener button                         (proxy [ActionListener] []                           (actionPerformed [evt]                                            (println message))))     (.. frame getContentPane (add button))     (.pack frame)     (.setVisible frame true)     frame))

(gui-sample/build-gui "Rock & Roll" "Hello, world!”)

50

ENRICO FRANCHI <[email protected]>

Page 51: Clojure Interoperability

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

51

ENRICO FRANCHI <[email protected]>

Page 52: Clojure Interoperability

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

52

ENRICO FRANCHI <[email protected]>

Page 53: Clojure Interoperability

GEN-CLASS (2)

(defn show [coll]   (if (seq coll)     (do       (print (first coll))       (recur (rest coll)))     (println "")))

(defn -show [coll]   (show coll)) (defn -foo [this coll]   (let [coll (seq coll)]     (if coll (count coll) -1)))

>>> import example >>> example.ClassExample.show([1, 2, 3]) 123 >>> ce = example.ClassExample() >>> ce.foo([1, 2, 3]) 3

53

Page 54: Clojure Interoperability

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

54

ENRICO FRANCHI <[email protected]>

Page 55: Clojure Interoperability

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

import clj py_list = ['a', 'b', 'c'] my_list = clj.ImmutableList(py_list)

55

ENRICO FRANCHI <[email protected]>

Page 56: Clojure Interoperability

IMPLEMENTING PYOBJECTS IN CLOJURE (2)

(defmacro delegate-to-state [sym] `(defn ~(symbol (str "-" sym)) [this#] (~sym (.state this#)))) (delegate-to-state peek) (delegate-to-state pop) (delegate-to-state count) (delegate-to-state empty) (delegate-to-state seq)

(defn -cons [this other] (cons (.state this) other)) (defn -equiv [this other] (= (.state this) other))

56

ENRICO FRANCHI <[email protected]>

Page 57: Clojure Interoperability

IMPLEMENTING PYOBJECTS IN CLOJURE (2)

(defmacro delegate-to-state [sym] `(defn ~(symbol (str "-" sym)) [this#] (~sym (.state this#)))) (delegate-to-state peek) (delegate-to-state pop) (delegate-to-state count) (delegate-to-state empty) (delegate-to-state seq)

(defn -cons [this other] (cons (.state this) other)) (defn -equiv [this other] (= (.state this) other)) print my_list.peek() print my_list.pop() print my_list.count() print my_list.empty() print my_list.seq() print my_list.cons('d') print my_list.equiv(py_list) print my_list.equiv(['a', 'b', 'c'])

57

ENRICO FRANCHI <[email protected]>

Page 58: Clojure Interoperability

IMPLEMENTING PYOBJECTS IN CLOJURE (3)

(defn -__finditem__ [this index] (let [index (if (instance? Number index) index (Py/tojava index Number))] (try (Py/java2py (nth (.state this) index)) (catch IndexOutOfBoundsException e (throw (Py/IndexError (.toString e)))))))

print my_list[0] print my_list[1] print my_list[2] print my_list[2.4] try: print my_list[3] except IndexError, e: print e try: print my_list['a'] except TypeError, e: print e try: my_list[0] = 1 except TypeError, e: print e

58

ENRICO FRANCHI <[email protected]>

Page 59: Clojure Interoperability

CLOJURE RT

source = ''' (ns rember) (defn rember [a lat] (cond (empty? lat) '() (= (first lat) a) (rest lat) :else (cons (first lat) (rember a (rest lat))))) '''

from clojure.lang import RT, Compiler Compiler.load(java.io.StringReader(source)) rember = RT.var('rember', 'rember') print rember.invoke(2, range(4))

59

ENRICO FRANCHI <[email protected]>

Page 60: Clojure Interoperability

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)

60

ENRICO FRANCHI <[email protected]>

Page 61: Clojure Interoperability

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

61

ENRICO FRANCHI <[email protected]>

Page 62: Clojure Interoperability

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

62

ENRICO FRANCHI <[email protected]>

Page 63: Clojure Interoperability

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

63

ENRICO FRANCHI <[email protected]>

Page 64: Clojure Interoperability

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

64

Page 65: Clojure Interoperability

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

class SpamAndEggs(object):     def __init__(self, eggs):         self.eggs = eggs     def hasSpam(self):         return True     def getEggs(self):         return self.eggs     def __repr__(self):         return 'Spam and %s eggs.' % self.eggs

65

Page 66: Clojure Interoperability

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

66

Page 67: Clojure Interoperability

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

67

ENRICO FRANCHI <[email protected]>

Page 68: Clojure Interoperability

MAKE-FACTORY (AGAIN)

68

ENRICO FRANCHI <[email protected]>

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

Page 69: Clojure Interoperability

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

69

ENRICO FRANCHI <[email protected]>

Page 70: Clojure Interoperability

Thanks for Your Kind Attention

CONCLUSION

https://github.com/rik0/PyCLJ_Examples https://github.com/rik0/pyclj

70

ENRICO FRANCHI <[email protected]>