Open
Description
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