ClojureScript

destructuring maps in protocol method signatures cause runtime values to be replaced with the destructuring map literal

Details

  • Type: Defect Defect
  • Status: Closed Closed
  • Priority: Minor Minor
  • Resolution: Declined
  • Affects Version/s: 1.9.76
  • Fix Version/s: None
  • Component/s: None
  • Labels:
  • Environment:
    Can be reproduced with the clojurescript Quick Start jar and the code below.

Description

(defprotocol P
(f [this {:keys [x]}]))

(defrecord R []
P
(f [this m] m))

(f (R.) {:hello "world"}) ;; => {:keys [:hello]}

We should get the same map as output as we used as input - instead, we get the destructuring map literal.

The above code works as expected in a bare Clojure 1.8 REPL.

Activity

Hide
Bert Muthalaly added a comment -

Oh, I think I understand what's happening here.

We unconditionally splice the method signature of the protocol into the generated call to (. this <slot> ~@sig), so defn correctly destructures the map, but the invocation of the method macroexpands to (. this cljs$user$P$mtd$arity$2 this {:keys [x]}).

Can be reproduced with

(cljs.pprint/pprint (macroexpand-1 '(defprotocol P (mtd [this {:keys [x]}]))))

at a bare ClojureScript REPL.

My original rationale for supporting this was for protocol readability.

It is sometimes useful to approximate named arguments in protocol method signatures, especially because keyword arguments are not supported in protocol method signatures due to the fact that keyword destructuring is variadic[0].

If you could write destructuring forms in protocol method signatures (that had no effect), a reader of a protocol method can quickly understand the desired invocation.

Other solutions:

  • Spec the protocol arguments. More robust, but the information is no longer inline with the signature of the protocol method.
  • Document this in the method docstring.
  • Warn that destructuring forms are not supported - solves the problem of the user's map being replaced by the destructuring map at runtime.

[0]: https://groups.google.com/forum/#!topic/clojure/AHfyzXCgvTk

Show
Bert Muthalaly added a comment - Oh, I think I understand what's happening here. We unconditionally splice the method signature of the protocol into the generated call to (. this <slot> ~@sig), so defn correctly destructures the map, but the invocation of the method macroexpands to (. this cljs$user$P$mtd$arity$2 this {:keys [x]}). Can be reproduced with (cljs.pprint/pprint (macroexpand-1 '(defprotocol P (mtd [this {:keys [x]}])))) at a bare ClojureScript REPL. My original rationale for supporting this was for protocol readability. It is sometimes useful to approximate named arguments in protocol method signatures, especially because keyword arguments are not supported in protocol method signatures due to the fact that keyword destructuring is variadic[0]. If you could write destructuring forms in protocol method signatures (that had no effect), a reader of a protocol method can quickly understand the desired invocation. Other solutions:
  • Spec the protocol arguments. More robust, but the information is no longer inline with the signature of the protocol method.
  • Document this in the method docstring.
  • Warn that destructuring forms are not supported - solves the problem of the user's map being replaced by the destructuring map at runtime.
[0]: https://groups.google.com/forum/#!topic/clojure/AHfyzXCgvTk
Hide
David Nolen added a comment -

It's invalid to use destructuring syntax in defprotocol

Show
David Nolen added a comment - It's invalid to use destructuring syntax in defprotocol

People

Vote (0)
Watch (0)

Dates

  • Created:
    Updated:
    Resolved: