Skip to content

Commit 4462095

Browse files
committedAug 11, 2017
Incorporate feedback
1 parent c8cb5af commit 4462095

File tree

1 file changed

+60
-51
lines changed

1 file changed

+60
-51
lines changed
 
+60-51
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
= Understanding Clojure's Polymorphism
1+
= Understanding Clojure's Datatype Constructs
22
Ikuru Kanuma
33
2017-07-20
44
:type: guides
@@ -10,63 +10,70 @@ ifdef::env-github,env-browser[:outfilesuffix: .adoc]
1010
== Goals of this guide
1111

1212
Clojure supports several constructs for speaking to the Java world
13-
and creating types for polymorphic dispatch. +
13+
and/or creating types for polymorphic dispatch. +
1414
Because these constructs have overlapping capabilities, it may be confusing to know which construct to use at a given situation. +
15-
Hopefully this guide clarifies what each construct is good at, while presenting minimal usage examples.
16-
17-
== Warm up with some Java
18-
19-
Let's warm up with some Java interop:
20-
21-
[source,clojure-repl]
22-
----
23-
user=> (import 'java.util.Date)
24-
java.util.Date
25-
user=> (.toString (Date.))
26-
"Fri Jul 21 11:40:49 JST 2017"
27-
----
28-
29-
Java Interop works. Cool!
15+
This guide clarifies what each construct is good at, while presenting minimal usage examples.
3016

3117
== Proxy a Java class and/or Interfaces
3218

33-
Say we want the .toString method to add a greeting at the beginning for friendlyness. +
34-
The proxy macro can be used to create an adhoc object that extends a Java Class:
19+
The proxy macro can be used to create an adhoc object that extends a Java Class.
20+
The example below extends the good old java.util.ArrayList such that a Clojure vector
21+
wrapped in an atom is used internally to manage state.
3522

3623
[source,clojure-repl]
3724
----
38-
user=> (def px (proxy [Date] []
39-
(toString []
40-
(str "Hello there! It is now "
41-
(proxy-super toString)))))
42-
user=> (.toString px)
43-
"Hello there! It is now Fri Jul 21 11:48:14 JST 2017"
25+
(import 'java.util.ArrayList)
26+
27+
(def px (let [atm (atom [])]
28+
(proxy [ArrayList] []
29+
(add [e]
30+
(swap! atm #(conj % e))
31+
true)
32+
(get [idx]
33+
(get @atm idx))
34+
(size [] (count @atm)))))
35+
36+
(dotimes [n 10]
37+
(.add px n))
38+
;; => nil
39+
(.get px 0)
40+
;; => 0
41+
(.get px 6)
42+
;; => 6
43+
(.size px)
44+
;; => 10
4445
----
4546
The ad hoc object can also implement Java Interfaces:
4647

4748
[source,clojure-repl]
4849
----
4950
(import 'java.io.Closeable)
5051
(import 'java.util.concurrent.Callable)
51-
user=> (def px (proxy [Date Callable Closeable] []
52-
(toString []
53-
(str "Hello there! It is now "
54-
(proxy-super toString)))
55-
(call []
56-
(prn "Someone called me!"))
57-
(close []
58-
(prn "closing!"))))
59-
user=> (.close px)
52+
53+
(def px (let [atm (atom [])]
54+
(proxy [ArrayList Closeable Callable] []
55+
(add [e]
56+
(swap! atm #(conj % e))
57+
true)
58+
(get [idx]
59+
(get @atm idx))
60+
(size [] (count @atm))
61+
(call []
62+
(prn "Someone called me!"))
63+
(close []
64+
(prn "closing!")))))
65+
66+
(.close px)
6067
"closing!"
6168
nil
62-
user=> (.call px)
69+
(.call px)
6370
"Someone called me!"
6471
nil
6572
----
6673

6774
== Leaving Java with defrecord
6875

69-
Sofar this is all dealing with Java stuff from Clojure. +
76+
So far this is all dealing with Java stuff from Clojure. +
7077
If we do not have to extend from a concrete Java Type, we can define our own types
7178
that implement interfaces (and protocols, coming up next!) from Clojure via the
7279
link:https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/defrecord[defrecord] macro:
@@ -83,37 +90,39 @@ user=> (.close (Foo. 2 2))
8390
nil
8491
----
8592

86-
Records are nicer for the reasons described in the https://clojure.org/reference/datatypes#_deftype_and_defrecord[reference].
93+
Records are nicer than Java classes for the reasons described in the https://clojure.org/reference/datatypes#_deftype_and_defrecord[reference].
8794

8895
https://clojure.github.io/clojure/clojure.core-api.html#clojure.core/deftype[deftype] is
8996
also available for implementing lower level constructs that require mutatable fields.
9097

9198
== Protocols; like Java Interfaces, but better
92-
https://clojure.org/reference/protocols[protocols] offer similar capabilities as Java interfaces, but is more powerfuld because:
99+
https://clojure.org/reference/protocols[Protocols] offer similar capabilities as Java interfaces, but are more powerful because:
93100

94-
* It is a cross platform construct
95-
* It allows third party types to participate in any protocols
101+
* They are a cross platform construct
102+
* They allow third party types to participate in any protocols
96103

97-
Let's make a protocol that handles Java Date instances as well as Foo records:
104+
Let's make a protocol that handles Java ArrayList instances as well as Foo records:
98105

99106
[source,clojure-repl]
100107
----
108+
user=> (defprotocol IBaz
109+
(baz [this]))
110+
101111
user=> (extend-protocol IBaz
102-
Date;;Thing from Java
112+
ArrayList ;;A Java Class
103113
(baz [this]
104-
(str "baz method for a Date: "
105-
(.toString this)))
106-
Foo;;Clojure Record
114+
"ArrayList Baz")
115+
Foo ;;A Clojure Record
107116
(baz [this]
108-
(str "baz method for a Foo record!")))
117+
"Foo Baz"))
109118
nil
110-
user=> (baz (Date.))
111-
"baz method for a Date: Fri Jul 21 14:04:46 JST 2017"
119+
user=> (baz (ArrayList.))
120+
"ArrayList Baz"
112121
user=> (baz (Foo. 1 1))
113-
"baz method for a Foo record!"
122+
"Foo Bax"
114123
----
115124

116-
The main thing to realize here is that protocols are more powerful than Interfaces because we are able to create custom abstraction for Types that we do not control (e.g. java.util.Date). +
125+
The main thing to realize here is that protocols are more powerful than interfaces because we are able to create custom abstraction for types that we do not control (e.g. java.util.Date). +
117126
If we were to apply a custom abstraction for Java Dates with an Interface IBaz,
118127
we must:
119128

@@ -150,4 +159,4 @@ To wrap up, here are some rules of thumb:
150159

151160
* Prefer protocols and records over Java Types; stay in Clojure
152161
* If you must extend a Java Class, use proxy
153-
* If you want a one-off implementation of a Protocol/Interface, use reify
162+
* If you want an anonymous implementation of a Protocol/Interface, use reify

0 commit comments

Comments
 (0)
Failed to load comments.