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
@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.
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.
I am running shadow-cljs
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.
I initially imagined having files for data/specs/components/react-app all separated
can I have multiple files all in the same namespace?
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.
ok, no it is 🙂 I want to keep it simple
I must be doing something silly again because I am unable to refer to my s/defs from the spec file/ns
Can you share some code to illustrate the problem? Is this in a project up on GitHub?
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]
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
😄 only half kidding though
OK. And what can't you do in the app file ns?
(sorry for the delay in responding -- was dealing with an issue someone just opened on clj-new that I needed to repro!)
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).
I feel bad if you apologize for such stuff, I realize people have lives beyond what is visible here : )
I had to do (def (s/def ... and it worked
for some reason I thought just (s/def would be enough
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
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.
gotcha
I have no idea what (def (s/def ...))
would do... but it's definitely not the right thing to do.
sorry, (def name-of-spec (s/def
That's what I assumed -- not the right thing to do.
so much black magic 😞
The "name-of-spec" is the keyword. That's how you refer to it in code that calls s/valid?
or something.
so specs can only be registered to keywords?
Yes.
docs said resolvable symbol k
I am confused, that's all
Link? So I can see what the context is.
how can I use :require :refer to import a spec definition?
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.
You can't refer in a single spec.
Once you've loaded the ns in which specs are defined, they are globally available.
They're not like Vars or functions.
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.
I can't even require them at this point
how to "load the ns", I usually just do :refer or :as, neither which seems to work here
Just require the namespace like any other. Then the specs are in the registry and available globally.
oh ok, so i can just write the ns :as, but then i have to completely ignore it
and instead always have to read the file of the spec, because ns-publics don't list it
Yeah, if you're not using the ::
auto-resolve syntax, you don't even need :as
and it's available via some (black, that is, unobservable) magic
(:require ... [my.specs] ...)
but how? just (:require [my.specs])?
ok 🙂
thanks again
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
Here's where the docs talk about the registry https://clojure.org/guides/spec#_registry -- don't know if that'll help?
it will help 🙂
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
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!
I see the big picture, I just don't see the details.
🙂 i've been trying to use clojure for 8 years at least
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 🙂
oh and because I can use shadow-cljs
so the tooling is solved more or less
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.
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[<http://java.io|java.io>.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.
Thanksit'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
you should be able to just use :foo/const or whatever instead
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.