6

I'm trying to extend library DomKM/silk.

Specifically there's deftype Route which implements protocol Pattern, which has method implementations, which I'd like to reuse in my custom implementation of Pattern protocol.

https://github.com/DomKM/silk/blob/master/src/domkm/silk.cljx#L355

(deftype Route [name pattern] Pattern (-match [this -url] (when-let [params (match pattern (url -url))] (assoc params ::name name ::pattern pattern))) (-unmatch [this params] (->> (dissoc params ::name ::pattern) (unmatch pattern) url)) (-match-validator [_] map?) (-unmatch-validators [_] {})) 

Ok so my implementation would look somehow like this, but I'd like to "inherit" Route's methods. I mean execute some custom logic first and then pass it to original Route methods.

(deftype MyRoute [name pattern] silk/Pattern (-match [this -url] true) ;match logic here (-unmatch [this {nm ::name :as params}] true) ;unmatch logic here (-match-validator [_] map?) (-unmatch-validators [_] {})) 

How is this done in clojure / clojurescript?

6
  • 1
    Clojure's designers are not fond of inheritance of methods. I don't think you can do this in a very natural way. You can obviously create a Route object and let MyRoute's methods delegate to the Route's. Objects created by deftype are Java classes, so you might be able do what you want by using one of the two Java interop macros that allow inheritance: proxy and gen-class. gen-class is probably overkill, so if you take this path, I suggest proxy. However, this goes against the design goals of Clojure, so you might want to simply reimplement the code you need. Commented Aug 24, 2015 at 4:50
  • The functions in Route are not large. If you want to do this a lot, you could write functions outside of the deftype objects and call them using Pattern methods. Commented Aug 24, 2015 at 4:50
  • @Mars Thanks for an answer! When I tried to use proxy: (proxy [Route] [name pattern] (-match [])) I get an error java.lang.VerifyError: Cannot inherit from final class. Is it even possible to use proxy on deftype? Commented Aug 24, 2015 at 5:20
  • Maybe not. I wasn't sure. It doesn't surprise me that it doesn't work, I guess. Although it doesn't say you can't extend deftypes, the "Datatypes and protocols are opinionated" section of Clojure's datatypes page will give you the idea. Clojure is intended to encourage certain kinds of programming, while retaining a lot of flexibility. Sometimes there's less flexibility than one wanted in a particular situation, but languages are full of tradeoffs. Commented Aug 24, 2015 at 13:12
  • 1
    I ended up using juxt/bidi. It's better designed for extensibility. I already achieved elegant solution for my problem with it. Thanks again Commented Aug 24, 2015 at 13:34

1 Answer 1

2

There's no need to redef the type. Referring to "ancestor" protocol implementation is possible, from another namespace.

user=> (ns ns1) nil ns1=> (defprotocol P (f [o])) P ns1=> (deftype T [] P (f [_] 1)) ns1.T ns1=> (f (T.)) 1 ns1=> (ns ns2) nil ns2=> (defprotocol P (f [o])) P ns2=> (extend-protocol P ns1.T (f [o] (+ 1 (ns1/f o)))) nil ns2=> (f (ns1.T.)) 2 

Keep in mind that ns1.P and ns2.P are totally different cats, both called P.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.