More Related Content Similar to Clojure Interoperability Similar to Clojure Interoperability (20) Clojure Interoperability2. 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>
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>