I may have missed the whole point about protocols but my question is, can protocols be used to dictate how to iterate a custom data structure or how println would print the object?
Assuming a map with two vectors,
{:a [] :b []}
When called first on it I would like to take from the :a vector but when conj on this structure i would like to conj to :b. Can I use protocols to achieve this type of behavior?
A protocol is a named set of named methods and their signatures, defined using defprotocol: (defprotocol AProtocol "A doc string for AProtocol abstraction" (bar [a b] "bar docs") (baz [a] [a b] [a b c] "baz docs")) No implementations are provided. Docs can be specified for the protocol and the functions.
Clojure collections "collect" values into compound values. There are four key Clojure collection types: vectors, lists, sets, and maps. Of those four collection types, vectors and lists are ordered.
Some things are still implemented as Java interfaces in Clojure; of those, I'd say some are likely to stay that way forever to ease cooperating with Clojure code from other JVM languages.
Fortunately, when defining a type using deftype
, you can have the new type implement any Java interfaces you require (which Brian mentioned in a comment above), as well as any methods of java.lang.Object
. An example to match your description might look like this:
(deftype Foo [a b]
clojure.lang.IPersistentCollection
(seq [self] (if (seq a) self nil))
(cons [self o] (Foo. a (conj b o)))
(empty [self] (Foo. [] []))
(equiv
[self o]
(if (instance? Foo o)
(and (= a (.a o))
(= b (.b o)))
false))
clojure.lang.ISeq
(first [self] (first a))
(next [self] (next a))
(more [self] (rest a))
Object
(toString [self] (str "Foo of a: " a ", b: " b)))
A sample of what you can do with it at the REPL:
user> (.toString (conj (conj (Foo. [] []) 1) 2))
"Foo of a: [], b: [1 2]"
user> (.toString (conj (conj (Foo. [:a :b] [0]) 1) 2))
"Foo of a: [:a :b], b: [0 1 2]"
user> (first (conj (conj (Foo. [:a :b] [0]) 1) 2))
:a
user> (Foo. [1 2 3] [:a :b :c])
(1 2 3)
Note that the REPL prints it as a seq; I believe that's because of the inline implementation of clojure.lang.ISeq
. You could skip it and replace the seq
method with one returning (seq a)
for a printed representation using the custom toString
. str
always uses toString
, though.
If you need custom behaviour of pr
family functions (including println
etc.), you'll have to look into implementing a custom print-method
for your type. print-method
is a multimethod defined in clojure.core
; have a look at core_print.clj in Clojure's sources for example implementations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With