Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

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

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.

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

No branches or pull requests

2 participants