Fork me on GitHub
#clojure-spec
<
2018-05-31
>
jumar11:05:01

@raymcdermott I've tried a limited version of your code and this seems to work (when tested with nc on command line):

(defn configured-prepl
  []
  (clj-server/io-prepl :valf identity))

(defn shared-prepl
  [opts]
  (let [socket-opts {:port          5555
                     :server-daemon false                   ; Keep the app running
                     :accept        `configured-prepl}]
    (let [server (clj-server/start-server (merge socket-opts opts))]
      (println "listening on port" (.getLocalPort ^ServerSocket server)))))
Run server:
(clj-server/remote-prepl "localhost" 5555)
Later on command line:
nc localhost 5555
(+ 1 1)
{:tag :ret, :val 2, :ns "user", :ms 0, :form "(+ 1 1)"}
(defn foo 1)
{:tag :ret, :val {:cause "Call to clojure.core/defn did not conform to spec:\nIn: [1] val: 1 fails spec: :clojure.core.specs.alpha/arg-list at: [:args :bs :arity-1 :args] predicate: vector?\nIn: [1] val: 1 fails spec: :clojure.core.specs.alpha/args+body at: [:args :bs :arity-n :bodies] predicate: (cat :args :clojure.core.specs.alpha/arg-list :body (alt :prepost+body (cat :prepost map? :body (+ any?)) :body (* any?)))\n", :via [{:type clojure.lang.Compiler$CompilerException, ...
For client written in Clojure I guess you should try remote-prepl as Alex suggested, but your problem seems to be related to the fact that read is not able to recognize #object in the response returned by remote repl. The error looks is caused by sth. like this:
(pr-str (Object.))
#object[java.lang.Object 0x66a64b49 "java.lang.Object@66a64b49"]

(read-string (pr-str (Object.)))
RuntimeException No reader function for tag object  clojure.lang.LispReader$CtorReader.readTagged (LispReader.java:1430)
The #object in response comes from spec failure (partial output, see the last line)
{:tag :ret, :val {:cause "Call to clojure.core/defn did not conform to spec:\nIn: [1] val: 1 
fails spec: :clojure.core.specs.alpha/arg-list at: [:args :bs :arity-1 :args] predicate: vector?\nIn: [1] 
val: 1 fails spec: :clojure.core.specs.alpha/args+body at: [:args :bs :arity-n :bodies] 
predicate: (cat :args :clojure.core.specs.alpha/arg-list :body (alt :prepost+body
 (cat :prepost map? :body (+ any?)) :body (* any?)))\n", :via [{:type clojure.lang.Compiler$CompilerException, 
:message "clojure.lang.ExceptionInfo: Call to clojure.core/defn did not conform to spec:\nIn: [1] 
val: 1 fails spec: :clojure.core.specs.alpha/arg-list at: [:args :bs :arity-1 :args] predicate: vector?\nIn: [1] 
val: 1 fails spec: :clojure.core.specs.alpha/args+body at: [:args :bs :arity-n :bodies] predicate: (cat :args :clojure.core.specs.alpha/arg-list 
:body (alt :prepost+body (cat :prepost map? :body (+ any?)) :body (* any?)))\n #:clojure.spec.alpha{:problems
 ({:path [:args :bs :arity-1 :args], :pred clojure.core/vector?, :val 1, :via [:clojure.core.specs.alpha/defn-args :clojure.core.specs.alpha/args+body
 :clojure.core.specs.alpha/arg-list :clojure.core.specs.alpha/arg-list], :in [1]}
 {:path [:args :bs :arity-n :bodies], :pred (clojure.spec.alpha/cat :args :clojure.core.specs.alpha/arg-list 
:body (clojure.spec.alpha/alt :prepost+body (clojure.spec.alpha/cat :prepost clojure.core/map? 
:body (clojure.spec.alpha/+ clojure.core/any?)) :body (clojure.spec.alpha/* clojure.core/any?))), :val 1,
 :via [:clojure.core.specs.alpha/defn-args :clojure.core.specs.alpha/args+body :clojure.core.specs.alpha/args+body], :in [1]}), 
:spec #object[clojure.spec.alpha$regex_spec_impl$reify__2436 0x233319e7 \"clojure.spec.alpha$regex_spec_impl$reify__2436@233319e7\"]
,,,
I'm not sure if this can be resolved somehow...

jumar11:05:15

That last bit I tested in separate repl:

(require '[clojure.core.server :as ccs])

user=> (ccs/remote-prepl "localhost" 5555 *in* #(prn %))
(+ 1 1)
{:tag :ret, :val 2, :ns "user", :ms 0, :form "(+ 1 1)"}

(defn foo 1)
Exception in thread "clojure.core.server/remote-prepl" clojure.lang.LispReader$ReaderException: java.lang.RuntimeException: No reader function for tag object
        at clojure.lang.LispReader.read(LispReader.java:304)
...

genRaiy11:05:29

yeah, he mentioned the valf option

genRaiy11:05:00

thanks for testing it out @jumar

genRaiy11:05:45

in theory with valf set to identity we should get a :data property and we can then read that directly

genRaiy11:05:01

so I’m going to play with a remote-prepl as you suggest

genRaiy11:05:10

tbh I’m struggling on what to give it as a Reader and an out-fn …. so I’m in :thinking_face: mode at the moment

genRaiy11:05:29

if you have any ideas … I am in the market 🙂

jumar11:05:47

@raymcdermott Couldn't find anything better than leveraging *default-data-reader-fn* somehow:

user=> (require '[clojure.core.server :as ccs])
nil
user=> (alter-var-root #'*default-data-reader-fn* (fn [_] (fn [tag value] value)))
#object[user$eval3$fn__138$fn__139 0xfd8294b "user$eval3$fn__138$fn__139@fd8294b"]

user=> (ccs/remote-prepl "localhost" 5555 *in* (fn [result] (if (-> result :val :cause) (prn (-> result :val :data)) (prn result))))

(+ 1 1)
{:tag :ret, :val 2, :ns "user", :ms 1, :form "(+ 1 1)"}

(defn foo 1)
{:clojure.spec.alpha/problems ({:path [:args :bs :arity-1 :args], :pred clojure.core/vector?, :val 1, :via [:clojure.core.specs.alpha/defn-args :clojure.core.specs.alpha/args+body :clojure.core.specs.alpha/arg-list :clojure.core.specs.alpha/arg-list], :in [1]} {:path [:args :bs :arity-n :bodies], :pred (clojure.spec.alpha/cat :args :clojure.core.specs.alpha/arg-list :body (clojure.spec.alpha/alt :prepost+body (clojure.spec.alpha/cat :prepost clojure.core/map? :body (clojure.spec.alpha/+ clojure.core/any?)) :body (clojure.spec.alpha/* clojure.core/any?))), :val 1, :via [:clojure.core.specs.alpha/defn-args :clojure.core.specs.alpha/args+body :clojure.core.specs.alpha/args+body], :in [1]}), :clojure.spec.alpha/spec [clojure.spec.alpha$regex_spec_impl$reify__2436 590551527 "clojure.spec.alpha$regex_spec_impl$reify__2436@233319e7"], :clojure.spec.alpha/value (foo 1), :clojure.spec.alpha/args (foo 1)}

4
genRaiy12:05:46

I didn’t know about that var, so thanks ….

genRaiy12:05:48

I really must look at all of those ear-muff things one day if I’m going to live down here 🙂

Alex Miller (Clojure team)13:05:00

This is interesting and probably ultimately needs a change in spec reporting. Rather than send the spec instance, it should be sending the spec name or form where you are seeing the object. For now, I think you’ll need to intercept either on the server or client side and either remove or transform it yourself

👍 4
😬 4
genRaiy22:05:58

do you consider this as a spec issue rather than a PREPL issue? Or a bit of both?

Alex Miller (Clojure team)22:05:55

Maybe prepl although it gives you the hook to address it

Alex Miller (Clojure team)13:05:52

I think hooking the default data reader to fall back to using tagged-literal is a pretty good dodge that covers many potential issues like this

👍 8
Andreas Liljeqvist13:05:52

How would I alias a namespace that isn't required? Circular dependency is a problem if I require it

Andreas Liljeqvist13:05:38

I want to keep a separate namespace/file for my spec but use another namespace

Andreas Liljeqvist13:05:43

Like my-namespace with functions and my-namespace.spec with the spec

Andreas Liljeqvist13:05:30

But using :t/id instead of having to write :my-namespace/id

guy13:05:08

So do you mean like. Inside of my-namespace you have some spec like (s/def :t/id) instead of (s/def ::id) or (s/def :my-namespace/id)

guy13:05:53

As far as i understood you have to require the namespace the spec is in, to use it in a different namespace. But i could be wrong

guy13:05:10

So you might want to just move your spec somewhere else to get around the circular dependencies? I ended up just giving certain specs fake namespaces to and not using ::id. Instead something like :user/id

Andreas Liljeqvist13:05:41

in my-namespace.spec I have a (s/def ::id nat-int?) . Resulting in :my-namespace.spec/id

Andreas Liljeqvist13:05:26

I would like the spec to remain in my-namespace.spec but be registered to :my-namespace/id

guy13:05:50

try this instead (s/def :my-namespace/id nat-int?)

Andreas Liljeqvist13:05:18

That will work, but it feels wrong when I have a lot of defs

guy13:05:32

You will still need to require my-namespace.spec in a different ns if you want to use the spec contained

guy13:05:48

but then you can refer to it like :my-namespace/id instead

guy13:05:52

I think my counter argument to "but it feels wrong when I have a lot of defs" would be its a bit redundant to have .spec in the spec name

guy13:05:13

:my-namespace.spec/id vs :my-namespace/id

guy13:05:23

like you know they are both specs

guy13:05:29

so is .spec really needed?

guy13:05:42

(this is just my opinion though, so someone else feel free to correct me etc)

Andreas Liljeqvist13:05:56

Only reason for the .spec is that I want to have a separate namespace for the specs vs the functions

guy13:05:30

Yeah i had user and user.spec too, because it felt nicer to separate them

guy13:05:38

yeah exactly the same thought process

guy13:05:56

But i ended up moving away from :: notation to something/id instead

Andreas Liljeqvist13:05:56

A bit worried about keyword collisions when doing it that way, but everything you say makes sense

guy13:05:30

I think thats more a case of you having a consistent naming convention really

guy13:05:40

>keyword collisions

Andreas Liljeqvist13:05:43

I would have prefered to do something like (alias 't 'my-namespace)

guy13:05:04

The troubles i was having were similarly named fields

guy13:05:13

user/name vs something else /name

guy13:05:29

so if u had a (s/def ::name) that was a string and one that was something else

guy13:05:36

it would cause headaches

guy13:05:56

if that makes sense?

Andreas Liljeqvist13:05:12

Yeah, but I might be using a third party library that also implement :user/name or something

Andreas Liljeqvist13:05:34

But that is a problem for future me 🙂 Thanks a lot for the help

guy13:05:40

sure but then you can prefix to to Andreas.user/name or Projectname.user/name etc :thumbsup:

guy13:05:47

nps! Good luck!

guy13:05:53

Hopefully i was helpful haha

Olical14:05:34

Afternoon. I was wondering if anyone else uses sets with their (s/keys...) calls?

Olical14:05:07

In the docs it mentions vectors, but wouldn't a set of keys make more sense, like: (s/keys :req-un #{::a ::b})?

Olical14:05:35

I ask because I've seen a weird issue where UUIDs are not being coerced by spec-tools if I use a set 😅

Olical14:05:37

You can reproduce it with

(s/def ::a (st/spec uuid?))
(s/def ::b (s/keys :req-un #{::a}))
(st/decode ::b {:a "4c6e852b-e8a3-4686-8916-4e345be53731"} st/json-transformer)
;; => {:a "4c6e852b-e8a3-4686-8916-4e345be53731"}
where st is spec-tools.core

Olical14:05:26

If you replace the set with a vector it works fine and coerces the UUID string into an actual UUID. I was thinking "maybe this is an issue with spec-tools", but if you're supposed to use a vector anyway this is technically okay, just a confusing gotcha.

ikitommi14:05:59

yes, the s/keys is parsed here: https://github.com/metosin/spec-tools/blob/master/src/spec_tools/parse.cljc#L112-L117. Hasn't been tested with sets. PR welcome ;)

Olical14:05:47

So @U050V1N74 and I have spotted it 😄

Olical14:05:56

It's because clojure.core/flatten on a set empties the set!

Olical14:05:01

TIL for the both of us.

Olical14:05:43

(flatten #{1 2 3}) gives you ()

Olical14:05:16

"because sets aren't sequential, they're only seqable"

ikitommi14:05:53

.. and ands

jarohen15:05:26

TIL ors and ands in s/keys are a thing, too

Andreas Liljeqvist14:05:07

Seems like https://dev.clojure.org/jira/browse/CLJ-2123 is related to my question about aliases