Fork me on GitHub
#clojure-spec
<
2020-04-22
>
ashnur01:04:36

should spec have their own ns an import app ns? or should the app import the spec ns? I am having this issue that circular dependencies seem unavoidable

seancorfield01:04:19

@ashnur The answer is "it depends". You should be able to find a partial ordering of dependencies that would allow you to split things into different namespaces -- probably three, so you have an "app" ns, a "spec" ns, and a "predicate" or "utility" ns that contains things the specs depend on, and the app can also depend on. But it may not be worth doing that analysis so having the specs in the same ns as your app code may just be easier.

seancorfield01:04:40

If you're writing code that you want to be able to run on older versions of Clojure(Script) that don't include spec, you have to break them out into optional namespaces so that users can opt in if they want the specs -- but that's really only for library writers.

ashnur01:04:28

I am running shadow-cljs

seancorfield01:04:40

At work, I try to have the data specs in a separate namespace (and do the work to untangle any problematic dependencies) but the function specs will generally go in the same namespace as the functions they are for.

ashnur01:04:57

I initially imagined having files for data/specs/components/react-app all separated

ashnur01:04:46

can I have multiple files all in the same namespace?

seancorfield01:04:30

The simple answer to that question is no. The complex answer is there are ways to spread a namespace across multiple files but I don't think it will solve the problem you have here.

ashnur01:04:05

ok, no it is 🙂 I want to keep it simple

ashnur01:04:47

I must be doing something silly again because I am unable to refer to my s/defs from the spec file/ns

seancorfield01:04:29

Can you share some code to illustrate the problem? Is this in a project up on GitHub?

ashnur01:04:51

in spec file I wanted to do something like (s/def :my.specs/one-spec identity) then in app file ns require [my.specs :as myspecs]

ashnur01:04:55

it's closed source but I can make a new repo with completely new code, it might just take a couple of weeks before i have the time

ashnur01:04:59

😄 only half kidding though

seancorfield02:04:38

OK. And what can't you do in the app file ns?

seancorfield02:04:08

(sorry for the delay in responding -- was dealing with an issue someone just opened on clj-new that I needed to repro!)

seancorfield02:04:06

In you app file ns, after that require, you should be able to reference that spec either as :my.specs/one-spec (i.e., its fully-qualified name) or ::myspecs/one-spec using the auto-resolve syntax which will expand ::myspecs to :my.specs using the alias of the namespace... the latter requires that the actual spec name really matches the spec ns in the qualifier. (and you could just (s/def ::one-spec identity) in the spec ns to have the spec automatically match the ns of the namespace it is defined in).

ashnur02:04:23

I feel bad if you apologize for such stuff, I realize people have lives beyond what is visible here : )

ashnur02:04:41

I had to do (def (s/def ... and it worked

ashnur02:04:51

for some reason I thought just (s/def would be enough

ashnur02:04:34

I am also in utter confusion about how keywords work, not just in spec but other places. I know it's a symbol that retuns itself, but apparently it also can hold specs

seancorfield02:04:47

s/def updates a registry behind the scenes which is essentially a map from keywords to spec objects. The keywords don't "hold" specs -- the association is done separately.

seancorfield02:04:37

I have no idea what (def (s/def ...)) would do... but it's definitely not the right thing to do.

ashnur02:04:23

sorry, (def name-of-spec (s/def

seancorfield02:04:44

That's what I assumed -- not the right thing to do.

ashnur02:04:23

so much black magic 😞

seancorfield02:04:28

The "name-of-spec" is the keyword. That's how you refer to it in code that calls s/valid? or something.

ashnur02:04:54

so specs can only be registered to keywords?

ashnur02:04:48

docs said resolvable symbol k

ashnur02:04:57

I am confused, that's all

seancorfield02:04:08

Link? So I can see what the context is.

ashnur02:04:45

how can I use :require :refer to import a spec definition?

seancorfield02:04:06

Ah, probably because of function specs: s/fdef uses the fully-qualified function name (symbol) so that's probably why s/def will accept a symbol.

seancorfield02:04:14

You can't refer in a single spec.

seancorfield02:04:38

Once you've loaded the ns in which specs are defined, they are globally available.

seancorfield02:04:49

They're not like Vars or functions.

seancorfield02:04:26

So you could require the specs ns in whatever you app entry point is and you would have access to those specs (as keywords) everywhere in your program.

ashnur02:04:12

I can't even require them at this point

ashnur02:04:41

how to "load the ns", I usually just do :refer or :as, neither which seems to work here

seancorfield02:04:21

Just require the namespace like any other. Then the specs are in the registry and available globally.

ashnur02:04:35

oh ok, so i can just write the ns :as, but then i have to completely ignore it

ashnur02:04:57

and instead always have to read the file of the spec, because ns-publics don't list it

seancorfield02:04:16

Yeah, if you're not using the :: auto-resolve syntax, you don't even need :as

ashnur02:04:16

and it's available via some (black, that is, unobservable) magic

seancorfield02:04:35

(:require ... [my.specs] ...)

ashnur02:04:41

but how? just (:require [my.specs])?

ashnur02:04:51

thanks again

ashnur02:04:27

when I think I understand something, it turns out that thing is specific to that particular area, and something else using the same syntax behaves completely differently

seancorfield02:04:42

Here's where the docs talk about the registry https://clojure.org/guides/spec#_registry -- don't know if that'll help?

ashnur02:04:55

it will help 🙂

ashnur02:04:39

the problem, which you have to realize is a problem, that I've had this page open for days and I have read it through from top to bottom and bottom to up several times

seancorfield02:04:38

Don't worry, sometimes it taking a lot of reading the Clojure docs before it actually makes sense and you get to see a big picture!

ashnur02:04:09

I see the big picture, I just don't see the details.

ashnur02:04:35

🙂 i've been trying to use clojure for 8 years at least

ashnur02:04:16

this time I will succeed because finaly I can do stuff like this, waking up at 1:30 am, then suffering for an hour before you come to help 🙂

ashnur02:04:26

oh and because I can use shadow-cljs

ashnur02:04:38

so the tooling is solved more or less

seancorfield02:04:14

I hear great things about shadow-cljs -- when (if) I ever go back to trying to do ClojureScript, that's the path I'll take.

dam13:04:23

Hello everyone, I am defining some specs in a .cljc file, some only exist in a #?(:clj (do ...)) , others in a #?(:cljs (do ...)) I get an error on a keyword at parsing time:

2. Unhandled clojure.lang.ExceptionInfo
   failed compiling
   file:/.../foo.cljc
   {:file
    #object[.File 0x5b1b23e7 "/.../foo.cljc"],
    :clojure.error/phase :compilation}
             compiler.cljc: 1717  cljs.compiler$compile_file$fn__3955/invoke

1. Caused by clojure.lang.ExceptionInfo
   /.../foo.cljc
   [line 52, col 41] Invalid keyword: ::const/env.
   {:type :reader-exception,
    :ex-kind :reader-error,
    :file
    "/.../foo.cljc",
    :line 52,
    :col 41} 
Here the ::const namespace is declared only for .clj parts of code (:require ... #?(:clj [... :as const])) When I split this .cljc into two files .clj and .cljs, there is no error. Would it be related to spec or is it a more general error? It is the first time that it happens. I really don't have any clue. Thanks

alexmiller14:04:14

it's more general - const relies on having a clojure runtime namespace context to resolve it - I'm not sure what the exact problem is without more context but generally you'll want to avoid these in reader conditionals

alexmiller14:04:01

you should be able to just use :foo/const or whatever instead

dam14:04:00

Thanks @alexmiller it works. It's not as convenient as the shortcut version but it's still better than two .clj and cljs files. If you want more info, don't hesitate to ask for. I'll send in a private message.