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 <[email protected]>
  • 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 <[email protected]>
  • 4.
    4 JVM LANGUAGES Jython Java ? JVM ENRICO FRANCHI <[email protected]>
  • 5.
    5 JVM LANGUAGES Jython Java Clojure JVM ENRICO FRANCHI <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 8.
    8 CLOJURE ~ Good Parts ~ Functional Programming Laziness Full Macros Multi-Methods Immutable types, STM & Concurrency Model ~ Bad Parts ~ “Java” ENRICO FRANCHI <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 33.
    33 CONCLUSION Thanks for Your Kind Attention https://github.com/rik0/PyCLJ_Examples https://github.com/rik0/pyclj ENRICO FRANCHI <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 38.
    38 JVM LANGUAGES Jython Java ? JVM ENRICO FRANCHI <[email protected]>
  • 39.
    39 JVM LANGUAGES Jython Java Clojure JVM ENRICO FRANCHI <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 42.
    42 CLOJURE ~ Good Parts ~ Functional Programming Laziness Full Macros Multi-Methods Immutable types, STM & Concurrency Model ~ Bad Parts ~ “Java” ENRICO FRANCHI <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 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 <[email protected]>
  • 70.
    70 CONCLUSION Thanks for Your Kind Attention https://github.com/rik0/PyCLJ_Examples https://github.com/rik0/pyclj ENRICO FRANCHI <[email protected]>