cider-nrepl icon indicating copy to clipboard operation
cider-nrepl copied to clipboard

`cider.nrepl.middleware.track-state` serializes the division function as `:/`

Open smee opened this issue 7 years ago • 5 comments

I'm developing a clojurescript application, so I'm using DIRAC as a clojurescript REPL in the browser. Since cider adds the cider.nrepl.middleware.track-state middleware to NREPL, dirac receives its messages in clojurescript. When it then parses the message via cljs.reader it chokes on the serialized form of the / symbol, the keyword :/ (for details please see https://github.com/binaryage/dirac/issues/52).

Since clojurescript adheres to the EDN standard, :/ is explicitely named as an invalid keyword. Clojure itself doesn't say the same thing, so a :/ keyword in clojure seems to be fine.

So my question is: What can I do in this situation?

smee avatar Jan 13 '17 14:01 smee

During what deserialization is this happening and how messages from cider middleware end up on DIRAC side in the first place?

I can only guess that this happens during the first eval response when a map of clojure.core is sent back by track-state middleware:

                                   arglists "([protocol])")
 ..                              (dict
                                   arglists "([x form] [x form & more])"
                                   doc      "\"form => fieldName-symbol or (instanceMethodName-symbol args*)\\n\\n  Expands into a member access (.) of the first member on the first\\n  argument, followed by the next member on the result, etc. For\\n  instance:\\n\\n  (.. System (getProperties) (get \\\"os.name\\\"))\\n\\n  expands to:\\n\\n  (. (. System (getProperties)) (get \\\"os.name\\\"))\\n\\n  but is easier to write, read, and understand.\""
                                   macro    "true")
 /                               (dict
                                   arglists "([x] [x y] [x y & more])"
                                   doc      "\"If no denominators are supplied, returns 1/numerator,\\n  else returns numerator divided by all of the denominators.\"")
 <                               (dict

but that dict's keys are string symbols (no : in front).

My best guess is that's the issue is on the DIRAC's side (or whatever comes before it). Something prepends ":" to the dict's keys and then DIRAC tries to read those transformed keys. In that case there is nothing that could be done on cider's side.

cc @darwin

vspinu avatar Aug 27 '17 13:08 vspinu

@vspinu Dirac implemented an nREPL client in Javascript (using ClojureScript). This page could help understanding the architecture: https://github.com/binaryage/dirac/blob/master/docs/about-repls.md#dirac

I think the problem @smee described is real. When someone serializes arbitrary data using clojure and then tries to deserialize them using cljs.reader, here is a minor risk of running into incompatibilities like the one described above.

But fixing this is would not really fix his setup as I wrote here. Because Dirac's nREPL client does not understand Cider nREPL messages.

Proper solution would be to run two nREPL servers with two separate sets of middleware. First one with Dirac middleware available for Dirac nREPL client(s) in Chrome. And second one with Cider middleware available for Cider nREPL client(s) in Emacs. This should work, but could introduce another issue of "syncing" nREPL state (Clojure(Script) compiler state) between those two servers. I'm not familiar with Cider and have never tried a similar setup myself.

darwin avatar Aug 27 '17 15:08 darwin

If anyone still cares, you can maybe hack your way around this by disabling the track-state middleware. Emacs shouldn't complain much (you'll just miss out on some small features).

To do that you would have to redefine the function cider.nrepl.middleware.track-state/make-transport to something like this.

(defn make-transport
  "NOOP to just return transport."
  [{:keys [^Transport transport]}]
  transport)

It might be enough to simply run this on the repl after it's loaded.

Malabarba avatar Oct 12 '17 00:10 Malabarba

Proper solution would be to run two nREPL servers with two separate sets of middleware. First one with Dirac middleware available for Dirac nREPL client(s) in Chrome. And second one with Cider middleware available for Cider nREPL client(s) in Emacs. This should work, but could introduce another issue of "syncing" nREPL state (Clojure(Script) compiler state) between those two servers. I'm not familiar with Cider and have never tried a similar setup myself.

@darwin Why would the unknown messages be problematic? I assume most clients would simply drop unknown responses.

bbatsov avatar Dec 09 '17 12:12 bbatsov

@bbatsov You are correct. In sane world clients should drop unknown messages and this would make them less fragile and add interoperability. But on the other hand they might silently fail in some scenarios when server misbehaves - maybe client would drop messages without giving user any error feedback.

Unfortunately messages don't have nice "tags" that they belong to specific middleware. My process-message is quite hairy as you can see here: https://github.com/binaryage/dirac/blob/aa2a66f8302f039c53ebc88a84869d51f79637d7/src/implant/dirac/implant/nrepl_tunnel_client.cljs#L75-L98

Message is a gray "goo of stuff". Maybe it is because I had to layer several nREPL hacks and the whole thing is a mess.

Anyways, I can put some effort into it and make it less fragile by tagging all dirac-related messages consistently and dropping everything else.

darwin avatar Dec 09 '17 14:12 darwin

autogenerated with https://github.com/MalloZup/doghub: issue inactive since 450 days. Please update the issue or close it

MalloZup avatar Aug 05 '19 21:08 MalloZup