-
-
Notifications
You must be signed in to change notification settings - Fork 292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Convention for necessarily required but not explicitly used namespaces #241
Comments
I like the metadata idea, because that's what it is—metadata on the require for other tools to use. It's explicit, and doesn't conflate existing patterns with implicit meaning. |
Conversation with @bbatsov on Slack: bozhidar: I prefer option 2. I totally agree we should document this in the style guide. I’ve many times deleted a namespace like this, because joker told me it was unused. 🙂
so maybe this wouldn’t be a problem, since ‘kept’ libspecs go on top anyways |
The #2 idea of using metadata seems the best to me definitely. The only thing is, I think the meta should be namespaced. Hasn't the style namespace been used before?
Or maybe a new linter namespace could be started:
Something like that. I'd find that with the namespace, not only we prevent clashes. But also, it is even more explicit. Someone can quickly understand that the meta isn't part of the actual program but only for tooling as style or linting hints. I'm really against #1 and #3 and #5. The ns macro is already so hard to comprehend and has so many rules. I don't want to add implicit tooling constraint on top. For the comment idea, #4, I think it's just a bit weird. Feels more hacky then meta. Doesn't feel like it integrates with the language as naturally. Also what if I also want to put a comment? And what about comment before the line instead of after? Basically, the comment seems like it requires whitespace awareness. That said, the escape form could be a good option, but your example of it is weird, I was thinking more:
This way, you use it like meta, but it's obvious that it's not program meta. It can apply to single forms, so whitespace doesn't matter, so you can do this:
Maybe let's call inline escaped form option #6 ? Another point to consider, what if we have a call to require out of the ns? Do tools remove those as well if they are unused? If so, should our solution work for those as well? For some reason it seems this doesn't work:
And I'd like to add a #7 option as well. Add a standard styling/linter config file. Personally, I hate adding extra hints that aren't actually part of the logic just for tooling. So if there was a file say In config file:
|
Unevals ( So far, the metadata option seems most popular. Namespacing it is a good idea, but until now I haven't seen any other tooling doing this, so clashes seem unlikely to me. If we were going to namespace it, I think word A unified Clojure linter config file format is maybe also good idea, but I think too wide in scope for this issue. Both joker and clj-kondo both support this already in their own config files. |
I like it. Don't care too much what the namespace is, just like it to be. So we protect against future clashes or possible clashes with program code and it makes it more explicit it is used by tooling and not the actual program. The namespace Config file can be added later. Also, I don't mind as much having to configure multiple config files, as long as the tooling has that option so I can keep my code clean and free of tooling concerns if I want too. So then my vote would be for |
One more thing though, what are the existing meta used by popular tooling? Maybe we should take that into consideration a bit to try and make them all somewhat coherent? |
You totally can add metadata to symbols.
|
Thanks. I must have confused this with keywords. |
Ya, but somehow, not with the literal syntax:
|
I’m tempted to suggest something more general like :ns/effectful to express intent rather than making it tied to the tooling aspect of it, but I can’t think of a non-tooling use case for it.
…Sent from my iPhone
On Jun 7, 2019, at 11:43 AM, Michiel Borkent ***@***.***> wrote:
You totally can add metadata to symbols.
Thanks. I must have confused this with keywords.
Since CLJS also supports strings as namespace names in libspecs, we still have the same problem though.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.
|
Don’t put anything between meta and symbol:
It totally works, Clojure uses that to extract type annotations from function name/local params etc |
So, I think Cider is the only thing I know that uses meta for tooling hint, and it does for formatting style using namespace key
So I think using An interesting this is that Cider have you specify the style at the function or macro level. That got me thinking, could we use meta on the namespace instead? That would solve all these issues we've been talking about:
These would now all be supported |
@didibus Metadata on the ns would leads to duplication of namespace symbols and that would lead to another type of linter I'm afraid, while we were trying to make life easier on tooling :-). On the other hand, I guess you would discover it soon enough if you misspelled one of those, by getting a warning you didn't expect. |
Would the |
Why not keep "side-effecting" requires outside of the ns macro ? Most people see the ns form as declarations of stuff to "compile" your code, so coming from others languages it feels natural to tidy those declarations. But when you want "side-effecting" requires, it is usually to set some state for your running program. It seems to me, that it should live outside of the ns form. Instead of adding new meta-keys convention on top of the language for tooling, maybe we just need to advise (in clojure style guide for example) the explicit usage of require out of the ns macro for runtime side-effects? |
Because that isn't possible in CLJS. With side-effect we mean loading specs, multimethods etc, so you are not using a function from those namespaces directly, but do need stuff to be registered. |
I was thinking about loading specs as well (setting the state of the spec registry). In that case I'll go for meta annotations because some formatting tools may move comments. |
Another option, and the one I would probably prefer, would be to allow the passing of this kind of metadata to |
@zane both clj-kondo and joker already support this in their config files ( |
@candid82 Ah, nice! I should have looked at the documentation more closely. |
@candid82 I agree that keeping the code uncluttered is preferable. Adding this metadata option has two reasons for existence:
|
@cursive-ide responded on Twitter: To summarize the current state of the discussion: it seems pretty much everyone is in favor of metadata ( |
I use 1 at the moment, but after thinking about the tradeoffs, I believe that 2 is the better option. |
Note that by placing a newline after the metadata marker, one would preserve vertical alignment: (:require
^:keep
[bar.specs]
[something.else]
^:keep
[foo.specs]
[clojure.string] |
what do you think about |
Maybe Unqualified Plus there's a |
I'm not a fan of |
Another option is: When there is no I think this works for 99% of my "implicit requires"? Example from a file that was in front of me right now: The implicit requires are |
That for me is definitely the best of all. If tools did this already I wouldn't need to use my require trick. It would imply if you wanted both the effects and a var, but expected the var might be moved at some point you'd need to do something like:
which seems fair enough to me. |
⬆️ for this, less is more and there is no need to agree on new metadata keywords. |
The only problem with this latest proposal is that you might still end up with wrong detection in case you forget an Maybe she two mechanisms for detecting this particular issue can cohexist? A wording on the namespace of the keywords: I would not go for a What about |
In that case you would end up with a false negative, which is probably better than all the false positives the tools are producing right now? I'm fine with |
Yeah...you are probably right on the former On the latter my only concern is that we end up being incoherent with the other annotations we have for indentation (is it We should aim at a specific one if we want to keep being specific or give both a broader scope like |
Sorry I realized my comment didn't add much to the conversations above. I will propose my variant:
Coherent with the current narrow scope (like style), concise and agreeing with @RickMoynihan on the terminology (plural because I can register more than one protocol for instance). |
I've merged I assume people voting for The namespace |
Initially I liked @borkdude's suggestion about doing it implicitly, but after giving it a bit more thought I actually think I'd prefer something more explicit. That makes it a lot easier for people who are unfamiliar with it to find out (search) what is going on, what the annotation is for.
So I guess I still like my initial suggestion of |
This can potentially be used by more than only linters. See the issue mentioned in the top post (clojure-emacs/refactor-nrepl#189). |
After having a bit of a side discussion on Slack, @borkdude and I came up with this suggestion:
Short, to the point, and clear, I like it :) |
There are three broad classes of readers of this metadata:
This is a tricky balancing act to be sure. I think that names like |
The newest clj-kondo already supports this feature. |
For people suggesting that the name should be about declaring that the namespace is effectful, can we name a single example use case which would rely on this which isn't just so things that clean up the ns form don't accidentally clean it up? Keep in mind this is a hint, so it should be assumed sometimes things might be labeled with it that aren't effectful as well. So the use case would need to be okay with that reality. Just like type hints are. |
I just thought of another name:
|
Yes. When a user loads a file in Cursive, I transitively find all its dependencies and also load them if they're out of date. I could use this to warn the user if they're accidentally pulling in a side-effecting namespace, or allow them to specify that they never want side-effecting ns'es loaded. |
@cursive-ide That seems different semantics than originally envisioned in this topic. E.g. a namespace that only registers multimethods should be loaded, but isn't explicitly used. You want to mark that as |
I like the current convention that required namespaces can be removed if not explicitly used in the code. Perhaps the tooling problem can be solved without changing the convention? For example, by code explicitly referencing such namespaces, using metadata to declare that a function depends on the side effects of the namespace. Take a re-frame app that requires a namespace declaring event handlers as side effects only, a recognized gotcha that can be resolved by explicitly referencing the namespace where it is used: (ns app.core
(:require
[re-frame.core]
[app.events]))
(defn ^{:require app.events} start []
(re-frame.core/dispatch-sync [:init])) The metadata declares that the |
@cursive-ide Hum, that's interesting. Just some thoughts:
|
June has ended so it's time to make up a summary of this issue before I close it.
But while voting, another option emerged, which is: if a libspec has no The latter option has been implemented in clj-kondo for a while now and personally I find it a satisfying solution. |
@borkdude thank you for all the hard work on this issue <3 |
@borkdude so to clarify, the outcome of this discussion was to not add any new metadata or keywords to the libspecs, and just rely on the convention that if there is no modifiers in the libspec then it is being required for side-effects? |
@danielcompton Correct. Example:
Of course tools are still free to pick the most popular metadata option, if they prefer that solution, but for clj-kondo I think this is sane default behavior. |
Ideas for expressing: this namespace is necessarily required but not explicitly used (loading specs, loading a foreign lib, multimethods, etc).
Problem
The problem is that some tooling warns or even removes required namespaces that are not explicitly used in the code. However, sometimes these namespaces are still used for registering specs, multimethods, etc. So there should be a way to say: we need these namespaces regardless of explicit usage.
Example:
Here specs related to
foo
are loaded, but the namespacefoo.specs
is not used explicitly.Related issues with earlier discussions:
Ideas:
Example:
Pro: simple, no clutter
Con: with this approach: there might be others that are accidentally also unwrapped and not reported.
Con: doesn't work for unused java imports (but these don't cause side effects anyway).
^:keep
on a vector in which the ns name is wrappedPro: works for requires + java imports
Pro: less accidental than 1
Con: breaks vertical alignment. However, it seems clojure-sort-ns already respects metadata:
so maybe this wouldn’t be a problem, since ‘keeped’ libspecs go on top anyways.
Also you can use a newline after the metadata to fix the vertical alignment.
Con: you always have to use a vector to place metadata on, since CLJS allows strings as namespace names in
:require
. This is not really a con, since this is already encouraged style in how to ns.:refer
vectorPro: less clutter than 2
Pro: less accidental than 1
Pro: less accidental than 1
Pro: better alignment than metadata
Con: clutter
Con: comments might not be seen by all tooling at the moment the AST is processed (clj-kondo included)
Pro: no clutter
Con: might be accidental
The text was updated successfully, but these errors were encountered: