Skip to content

Protocols implementation affordances using defrecord/deftype vs. extend-protocol #216

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
raymcdermott opened this issue Sep 16, 2017 · 3 comments

Comments

@raymcdermott
Copy link

When implementing Protocols using defrecord there are some missing affordances.

As an example, :pre and :post conditions cannot be applied.

[ Yes, I know we will soon have spec but these affordances will not be deprecated AFAIK. ]

Affordances not available on defrecord or deftype

(defprotocol Squarer (square [x]))

(defrecord PosIntSquarer [x]
  Squarer
  (square [_] (* x x)))

; Usage
(defprotocol Squarer (square [x]))
;=> Squarer

(defrecord PosIntSquarer [x]
  Squarer
  (square [_] (* x x)))
;=> practice1.core.PosIntSquarer

(square (->PosIntSquarer 2))
;=> 4
;---^^^ All good

(square (->PosIntSquarer "A"))
;CompilerException java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number, compiling:(.../core.clj:162:1) 
;---^^^ We would like to prevent this using a simple assertion

(defrecord PosIntSquarer [x]
  Squarer
  (square [_]
    {:pre  [(pos-int? x)]}
    (* x x)))
;=> practice1.core.PosIntSquarer

(square (->PosIntSquarer "A"))
;CompilerException java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number, compiling:(.../core.clj:162:1) 
;---^^^ The pre-condition is being ignored 

(defrecord PosIntSquarer [x]
  Squarer
  (square [_]
    {:pre  [(pos-int? x)]
     :post [(pos? %)]}
    (* x x)))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: % in this context, compiling:(.../core.clj:158:13) 
;---^^^ The post condition compilation fail shows that all affordances are not available

; Calling externally declared functions is one solution

(defn positive-square [x]
  {:pre  [(pos-int? x)]
   :post [(pos? %)]}
  (* x x))
;=> #'practice1.core/positive-square

(defrecord PosIntSquarer [x]
  Squarer
  (square [_]
    (positive-square x)))
;=> practice1.core.PosIntSquarer

(square (->PosIntSquarer "A"))
;CompilerException java.lang.AssertionError: Assert failed: (pos-int? x), compiling:(.../core.clj:163:1) 

Affordances are available via extend-protocol

(defrecord DirectSquarer [x])
=> practice1.core.DirectSquarer
(extend-protocol Squarer
  DirectSquarer
  (square [this]
    {:pre  [(pos-int? (:x this))]
     :post [(pos? %)]}
    (* (:x this) (:x this))))
;=> nil

(square (->PosIntSquarer 2))
;=> 4

(square (->PosIntSquarer "A"))
;CompilerException java.lang.AssertionError: Assert failed: (pos-int? x), compiling:(.../core.clj:178:1) 

(square (->PosIntSquarer -2))
;CompilerException java.lang.AssertionError: Assert failed: (pos-int? x), compiling:(.../core.clj:176:1) 

I don't want to claim this is a bug. But it's a sign that these options have pros and cons which are not currently well explained.

I would like to develop a more comprehensive table of features / affordances might be nicer than the current bullet list

@puredanger
Copy link
Member

Thanks for all this Ray - just want to let you know I see this and the other issue and would like to make these things better, but will likely not have time to look at these properly till post-Conj.

@raymcdermott
Copy link
Author

Great! There's no hurry :)

In the spirit of reducing your workload, can I do anything to move it forward independently?

PS. I have signed the RHCA

@puredanger
Copy link
Member

I would love to have a more comprehensive overview of where things like varargs / multiarity / prepost / etc are available across the different kinds of invocable things (functions, macros, protocols, multimethods, ??). All of this is good content - what would help is having a PR I can review.

I think most of this stuff would be more applicable on a Protocols guide (not the reference page, although a brief statement there might also be ok), but we don't have such a thing right now.

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

No branches or pull requests

2 participants