This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2020-05-28
Channels
- # babashka (29)
- # beginners (179)
- # bristol-clojurians (1)
- # calva (9)
- # chlorine-clover (47)
- # cider (57)
- # clj-kondo (1)
- # cljs-dev (13)
- # clojure (241)
- # clojure-europe (9)
- # clojure-nl (4)
- # clojure-norway (88)
- # clojure-spec (4)
- # clojure-uk (15)
- # clojurescript (211)
- # clojutre (1)
- # community-development (8)
- # core-async (1)
- # datomic (31)
- # figwheel-main (33)
- # fulcro (29)
- # ghostwheel (6)
- # graalvm (11)
- # graphql (12)
- # instaparse (4)
- # jobs (1)
- # jobs-discuss (17)
- # leiningen (7)
- # malli (6)
- # meander (38)
- # off-topic (208)
- # onyx (6)
- # re-frame (23)
- # reagent (8)
- # shadow-cljs (61)
- # spacemacs (10)
- # sql (5)
- # yada (5)
What’s the “goto” strategy for requiring a namespace only for the side effects (defmethod...). If I put in the “ns” macro, tooling will try to remove it. So currently, I am calling the require function directly
Yeah I agree
Although, not exactly what i was hoping for
What clj-kondo did I think is the right thing. It will leave alone requires that have no alias and no refer
> the convention that if there is no modifiers in the libspec then it is being required for side-effects
There was a huge discussion around it: https://github.com/borkdude/clj-kondo/issues/241
What require's are not done for side effects? That is, if you think of def
or defn
as having a side effect of creating a new Clojure var, then most require
's cause side effects, yes?
Side effects here means ... what exactly?
It means that the namespace require is needed even though it is not refered anywhere in namesapce
For a reloaded style workflow where you want to require it again after editing the underlying source code files, I guess?
So like when you don't actually use any var from the required namespace, but still need it to be required
What tooling tries to remove such a require (if you know)?
borkdude was trying to come up with a metadata convention for tools, which I think would still be nice. There were proposals, and votes. The votes mentioned that people liked using the namespace :tooling
for meta keys to instruct tooling. And for this use case, the vote was to have ^:tooling/keep
as meta. But there was so much different opinions, that I think going with nothing and just assuming that no libspec = used for side effect really made sense
"clean the namespace" meaning "fix up an ns form in a file to remove unnecessary things, and add needed things, guessed by scanning the rest of the code later in the file"?
Which will add missing requires. But it also has one to clean, which won't add missing, just remove unused, collapse duplicate, sort, etc.
Its pretty sweet: https://practicalli.github.io/spacemacs/images/add-missing-libspec.gif
Its triggered automatically sometimes as well, like if you just type something/foo when you press space after it, if it finds a something/foo somewhere it'll auto add the require for it
Clojure/Java is certainly dynamic enough that such a tool seems guaranteed to do undesired things in some cases.
Well, they don't all do the add what is missing. But they mostly do the remove what is not used
> Cider and Cursive do that that's a correct and conservative thing to do, but there will be false negatives. It kinda assumes you use aliases for everything. Some teams don't alias every required ns
Oh sorry, I mean that they both remove what they believe is unused. I actually don't think they follow that convention.n.
Actually might be wrong about Cursive, I guess its just cider, which under the hood uses refactor-clj
So then Cursive seem to follow this convention as well, which I think is the right way to go
I feel its easy, because you can say: If you use a required var through a fully qualified access, that's a warn for using an alias or refer instead. And if you have a require with no alias or refer, and you don't have any fully qualified use of it, then you can assume its there for side effects.
it'd be an opinionated pr that would introduce false negatives
there currently are false positives, which isn't a good thing either
my personal approach to this problem is making the defmethod
side-effect very explicit by making it part of a Component/Integrant start
method. Which also solves the additional problem of changed defmethods not being code-reloaded
That’s what was proposed. However, for projects started without those conventions it may introduce new discussions
Cool! In case it helps I have authored a very thin helper around multimethod definition: https://github.com/nedap/utils.modular#nedaputilsmodularapiadd-method The implementation is really minimal, one could trivially implement the same without an external library.
I mean, that's fair. But I'd rather a "clean ns" refactor leaves unused things behind, then remove things that then fail to compile or throw runtime errors
There are other cases for side effecting require. More so in ClojureScript, where you need to require a JS lib which some component you use depends on
Some people were saying to just put those side-effect require outside the ns, like using (require ...)
. But in ClojureScript that is not allowed. And that breaks other tooling assumptions. Like tools for auto-complete that look to the ns form to know what the namespace depends on
@potetm nope
> What is the advantage of that?
by placing side-effects in start
you avoid cleanup false postives and ensure the latest code definition is the one that gets loaded
eh, you can get that exact behavior and more from tools.namespace, but it makes some sense
no, t.n isn't smart enough to analyze defmethods, extend* calls etc. It just builds a dep tree out of requires
yes, but that's not gonna prevent avoid cleanup false positives
(tooling that cleans up unused requires)
I guess maybe one could use load-namespace in cljs and load in clojure to just load for side effect. Hum... That might be a good option as well. The intent is there too, we just want it loaded, its not that we require it explicitly.
Well, at that point, we're just back at a require without a libspec. I mean, nobody would know if the load is needed or not. So we would just trust you really need it to be loaded. Same with a require without libspec.
And that's why I think that's the best otion. Even if we tag with metadata, like ^:tooling/keep, nothing guarantees that it is truly needed. Maybe someone had added the meta a year ago, and since its no longer needed, but it lingers.
In my mind, there are roughly two phases in my clj program: 1. building program state (def vars, register multimethods, etc), 2. execution That’s not strictly true (for good reason), but most programs would do fine to make that distinction.
Ya I agree. Keeping it all in ns
is definitly more readable. I mean, if its easier for the tools to understand, so is it for humans
@potetm it's a fair thing to think, but clojure is quite runtime-oriented
defmethod
doesn't trigger any compiler magic. It's just a java call to .addMethod
https://github.com/clojure/clojure/blob/38bafca9e76cd6625d8dce5fb6d16b87845c8b9d/src/clj/clojure/core.clj#L1783 . i.e. like any other java interop you might use in your programs
Placing it in a non-conventional place like Component/Integrant start
makes this runtime-orientation very evident
> That’s not strictly true (for good reason), but most programs would do fine to make that distinction.
Ya, I think to some extent, ClojureScript shows us you can get pretty far without all the dynamism. So for example, it doesn't let you require things arbitrarily at runtime, and it has no vars. Well... okay, its actually really annoying that it has no Var 😛
By any chance, does lein uberjar
ignore resource files that start with .
? It doesn't seem to pick up what I have in resources/.s3-conf/files.edn
@dromar56 That does appear to be the case, based on a quick test locally.
Thanks for confirming. It seems like something that should be configurable in lein, but I can't find where, I'll continue looking
Yup, was just about to paste that link -- sorry, got distracted by feeding the cats. So you can override that in project.clj
(not sure what the effect of not excluding /.
will be, assuming that's what it is trying to exclude)
If I wanted to publish a Clojure lib, I'm trying to figure out what's the way to go. In Clojar, can you just choose what organization to use? Some libs don't seem to have one at all, what's the deal with this?
In Clojars, you can reserve as many prefixes as you want. A prefix is yours as soon as you publish some prefix/lib with it. You cannot take someone else's prefix. None can take your prefix.
Hum I see. Cause I'm thinking I want to follow Rich Hickey's advice, and use a DNS I own as my namespace. So it be my blog.project
, and thus my org for my lib would also be blog/project
But this would not match with my github account, and thus my github repo would be didibus/project
So it seems I can, so Clojar is good. Now there's just github, I wonder if I should create an organization, and then I can add my account as admin to it, and effectively then use my account to manage blog/project repos ?
Not answering the Github question, but in Clojars you can follow Rich's advice and do com.github.didibus/blog
(for example). There is no check that verifies the prefix belongs to you. As long as no one claimed it before you, you are free to claim it.
Right, I mean, I don't want to use my github alias. I'd like to use my own DNS I own, which is rubberducking (which is the dns I own for my blog)
There is absolutely zero relation between what you claim in Clojars and how you manage your repos.
so I'd like: rubberducking/poject
in clojar, but managed by my didibus account. And ideally, I'd want rubberducking/project
as the repo in gtihub as well, still managed by my didibus account
hum... I guess you're right about the com
, if I don't put that as well, its not really a DNS I own, since it could be http://rubberducking.org etc
Yeah, that will work. Clojars takes the prefix from POM's groupId or the prefix in deps.edn, project.clj etc.
I guess it doesn't matter if the repo in github is under a different name. In theory, a project repo can change place, like I could switch it to gitlab in the future, etc.
Though I guess com.github.didibus/project wouldn't be that bad either. :thinking_face:
No problem. Github orgs are easy too. Just create one. Mostly when you'd like to dissociate the project from your name (to give a sense that the project is not owned by you).
Yeah, I went with com.github.hindol as I own it permanently. I won't delete my Github account ever.
I name the Clojure namespaces whatever I want and the user has freedom to alias it. A long name only in deps.edn is not so bad. In fact it is good.
Ya, I think I was thinking my DNS is like an even more reserved name, since I actually paid for it 😛 But its not like I expect github to go away anytime soon.
I was thnking my namespaces would also be the same, though I guess I could drop the com.github.
I never include my username in the namespace name. I would just name the namespace blog.core etc.
There is the risk of clash, that's why people dream up unique names like reitit, sieppari, yada...
Feel its pretty generic, and I don't know, could clash one day. Maybe I'm too worried about it
Ya, but, I don't know, I feel like I never really type it anyways, it auto-completes, or I copy/paste it
What I'm thinking is if one day it does clash, as a user, you're just screwed, like, there's absolutly no workaround
So say rubberducking is my "org". All my projects would be rubberducking.bla
. Or I guess I could come up even with some new name for it. Like what the funcool guy does
This is why clj-new
encourages projects to be called username/project
or orgname/project
(where username or orgname are from GitHub) and it puts those in pom.xml
as the group/artifact name and it creates src/username/project.clj
or src/orgname/project.clj
as the main namespace file 🙂
Ya, it make sense. But after further looking. It seems neither lambdaisland nor funcool putbtheirnorg name in the namespace
When working with clojure i find it easiest to debug with a mixture of things. One i method i come back to a lot is just using atom to capture state. This is great except in practice i do all the fiddling right in the ns with the code. Which can get distracting and messy sometimes. I'm trying to think of an alternative method.
I combine the atom for tracking (eg. vector which I use swap! to conj to), with tap>
so that I am only adding tap calls in the target ns
and all the debugging defs can be in user or whatever
another useful idiom for this is using a map, with a namespaced keyword matching the function being debugged
(defn foo [& args] (tap> {:context ::foo :args args ... ...}) ...)
is there a good resource covering tap>
?the docs seems sparse, https://clojuredocs.org/clojure.core/tap%3E
I think regarding "resource", all you need is: use tap> to offer data use add-tap to register a function that should consume that data (async)
sweet. I’ve done my own adhoc versions of this before.
this seems better
are there any libraries that build on top of this?
I know rebl can integrate
I hope that every debugging / logging / inspection lib would integrate going forward
yea, seems super useful
a funny side effect of this is that I can end up with dangling tap>
calls in my namespace if I'm not careful, but it's a no-op if no function is registered to consume it, so it's less of a problem than println
which has the same issue
I can’t count the number of times I’ve written some variant of: > (def my-log (atom [])) > (defn log! [val] > (swap! my-log conj val))
right - now that's in my user.clj
in my home dir, and I can just register it with add-tap
this reminds me I should make a fireplace shortcut to load-file
~/user.clj rather than relying on classpath stuff
my ongoing project has been a library for desktop gui development in clj. building dev/debug tools for clojure seems like a good place to showcase what’s possible. thanks for tap>
tip!
the one thing I would have done differently would be to have tap>
return the thing you pass to it, the workaround being (doto x tap>)
of course
tap>
returns true
if it succeeds and false
if it fails -- which I believe only happens if the buffer it uses is full behind the scenes?
(according to the source, yes, there's a limited size queue behind it)
(java.util.concurrent.ArrayBlockingQueue. 1024)
The "overhead" of calling tap>
if no one is listening is to start a daemon thread that sits there trying to read from that queue.
and my duckduckgo-fu is failing me
There is a little bit more in the changes in Clojure 1.10.0 release notes here: https://github.com/clojure/clojure/blob/master/changes.md#23-tap
it’s been a while since I’ve checked the change list. lots of other goodies in here too
I added a link to that to the http://ClojureDocs.org page for tap>, and see also links between tap> add-tap remove-tap. 2 minutes, and done! (with that minimal level of info, at least, not actually in-depth docs)
I think a real gotcha here is that the amount of improvement / utility offered is massive, compared to the utter simplicity of using them, so you expect more doc than you actually need :D
it's very simple, the most useful thing is it decouples offering data to debug from the code doing the debugging
I've gotten into the habit of asserting what I want the state to be instead of capturing the state
all you need is tap>, add-tap, remove-tap
I know I can mark functions as deprecated, but can protocols include deprecation info? Something like this
(defprotocol Foo
(test ^:deprecated [args...] "doc string"))
(defprotocol Foo (^:deprecated test [args...] "doc string"))
(meta #'test)
Hi! When using git coordinates as a dependency, why is it only possible to select a specific sha? When I have a shared library that is also being actively developed, it would be nice to just set a branch-name instead of a sha. Is there a reason for not being able to use any kind of reference to a commit?
A branch name is not a reference to a specific commit, is it?
It is a reference to a specific commit. It is just a reference that changes over time. I don't see why that is a problem.
it breaks the cache
also, because it moves, it does not give you a reproducible build and there are security and other implications to that
like someone changing their branch to point to something that mines bitcoin
all that said, the dev scenario is one where this would be useful and I happen to be in the same place right now so I've been thinking about ways to make this work with some kind of guards around it
maven doesn't have git dependencies
basically SHAs are values and maven versions, git tags, git branches, are all references
maven artifact versions don't change out from under you - they are immutable
well that's due to version selection, which is an independent problem
(though you would draw a lot of ire from users if you re-cut different code with the same maven version or git tag)
there are a couple of exceptions, snapshots (which also break clj's cache) and special virtual versions like RELEASE or LATEST
(which deps does not explicitly support and Maven has been phasing out)
but Maven does not allow you to, for example, deploy an artifact based on snapshots
so there are some guards there
Yes, all I am saying is that these problems are not really specific to git dependencies
Is it fair to say that a design goal of tools deps is to avoid these problems?
it's an area that does not yet have good solutions in clj
we just haven't tried to solve them yet
@U09LZR36F Will try that, thanks!
local deps have their own caveats (deps don't see changes to transitive local deps.edn files, so you need to -Sforce a classpath update), but yes that is an option
@U013G4ZKQTW to be honest, it's saved me a few times where I've forgotten a project is using it.
@U064X3EF3 for sure. But I think that feels more like a bug, vs not supporting mutable artefact references which is in many ways an anti feature.
@U064X3EF3 Would it be possible to keep caching git deps on SHA and then when resolving a git dependency we turn the reference into a sha and look for that in the cache?
there are a few different caches involved, the important one being a cache of the built classpath that is based on hashing the deps.edn file, clj can start quickly if it doesn't need to compute a new classpath
a feature that relies on parsing the deps.edn file already busts that most pivotal cache
yes, that
even that is probably solveable but this is all just solution space wankery. needs to be unrolled all the way back to the problem for better thinking, which is not something I have time to do right now
Is it possible to make a transducer that composes with other transducers but lets them work on just the v
for pair values [k v]
? I keep writing (map (fn [[k v]] [k (my-f v)]))
where map could be map, mapcat, filter, remove, etc
@noisesmith @hiredman I think I made the transducer,
Example usage. Looks like I'll have to do something like map-longest
so first returning 3 elements and second returning 2 elements produces 3 pairs instead of 2
isn't that more verbose than the direct implementation via mapcat would be?
Yeah, I'm going to break it down into multiple transducers / functions.
(mapcat (fn [[a b]] [a (repeat b 3)]) [[1 2]])
would produce [a b b b]
but I want [[a b] [a b] [a b]]
a helper for that is pretty easy to write
(defn update-val [f] (fn [entry] [(key entry) (f (val entry))]))
so you end up with (map (update-val my-f))
true, and I don't even know what you'd expect mapcat to do here
for filter / remove all you need is (comp my-f val)
mapcat
would be like (map vector (repeat k) (f v))
The entries are 2 element vectors so key
and val
don't work
hi everyone, Lisp was my first language and I'm trying to learn Clojure. Glad to find this community!
@kyle OK - that change is easy enough to adapt, but back to the original question no there's nothing built in like this
Okay, I think I'll write some higher order functions and still use map/mapcat/filter so something like (filter (value-pred my-pred))
and (map (transform-value my-f))
General interceptor question: Is it recommended\preferred to lay out the entire stack of of interceptors and opt out as errors or branches are encoutered or is it considered better to have them conditionally add to the queue?
Hmm... nevermind. After writing it out I think option A is much better. Otherwise, it'd be like using ->
on one form and expecting that to chain the next one and keep doing that. It would work, but not be composable or paint a clear picture of the actual pipeline.
@noisesmith @hiredman I think I made the transducer,