Protocols

Read this first!

Clojure issue tracking now lives at http://dev.clojure.org/jira, and the wiki is at http://dev.clojure.org. These Assembla pages are kept online for historical interest only.

Protocols

This page is a design scratchpad. Please see:

http://clojure.org/protocols

for the current documentation.

There are several motivations for protocols:

Basics

A protocol is a named set of named methods and their signatures:

(defprotocol AProtocol
  "A doc string for AProtocol abstraction"
  (bar [a b] "bar docs")
  (baz ([a] [a b] [a b c]) "baz docs"))

 

defprotocol will automatically generate a corresponding interface, with the same name as the protocol, i.e. given a protocol my.ns/Protocol, an interface my.ns.MyProtocol. The interface will have methods corresponding to the protocol functions, and the protocol will automatically work with instances of the interface.

Note that you do not need to use this interface with deftype or reify, as they support protocols directly:

  (defprotocol P 
    (foo [x]) 
    (bar-me ([x] [x y])))

  (deftype Foo [a b c] [P]
    (foo [x] a)
    (bar-me [x] b)
    (bar-me [x y] (+ c y)))
  
  (bar-me (Foo 1 2 3) 42)

  (foo 
    (let [x 42]
      (reify [P] 
        (foo [this] 17)
        (bar-me [this] x)
        (bar-me [this y] x))))

But a Java client looking to participate in the protocol can do so most efficiently by implementing the protocol-generated interface.

External implementations of the protocol (which are needed when you want a class or deftype not in your control to participate in the protocol) can be provided using the extend construct:

(extend ::AType ;or AClass or AnInterface 
  AProtocol
   {:foo an-existing-fn
    :bar (fn [a b] ...)
    :baz (fn ([a]...) ([a b] ...)...)}
  BProtocol 
    {...} 
  ...)

 

Protocols are fully reified and will support reflective capabilities.

  (extend-type ::MyType 
    Countable
      (cnt [c] ...)
    Foo
      (bar [x y] ...)
      (baz ([x] ...) ([x y zs] ...)))

  expands into:

  (extend ::MyType
   Countable
     {:cnt (fn [c] ...)}
   Foo
     {:baz (fn ([x] ...) ([x y zs] ...))
      :bar (fn [x y] ...)})
  • See also: extends?, satisfies?, extenders
  • Prerelease changes under consideration

    Old ideas/scratchpad

     

    Issues

     

    Other ideas

    Constraints

     

    Multiprotocols

    These would allow the full power of multimethods, including arbitrary dispatch functions, with the proviso that the set be implemented in terms of the same dispatch value. Note the dispatch fn supplied per method in the protocol def:

    (defmultiprotocol AProtocol
      "A doc string for AProtocol abstraction"
      (bar dispatch-fn [a b] "bar docs")
      (baz dispatch-fn ([a] [a b] [a b & c]) "baz docs"))

    Implementations of the multiprotocol methods can be provided using the implement construct, note the protocol is implemented in terms of a single dispatch value:

    (implement AMultiProtocol adispatchvalue
     {:foo an-existing-fn
      :bar (fn [a b] ...)
      :baz (fn ([a]...) ([a b] ...)...)})