beginners

Henry 2025-06-24T12:13:42.622999Z

I wrote some code which extends the capability of a library I am using. What is the best practice for structuring folders/namespaces in this case - e.g. when the lib is called foo would you add this into the foo namespace, separate from the main project's code? E.g. when main code lives in src/company/my_project/**.clj , would you place the extension code in src/foo/new_stuff.clj ?

p-himik 2025-06-24T12:16:08.276799Z

Definitely not as foo can potentially add new_stuff.clj itself and things will break on your end then. Apart from that, it doesn't really matter since it doesn't seem like you will be distributing that code for use by others. Could be src/company/foo/new_stuff.clj, could be src/company/foo_new_stuff.clj, could be src/company_foo/new_stuff.clj - as long as it's more or less clear what's going on.

Henry 2025-06-24T12:18:55.408389Z

great - thanks. FWIW I did actually make a PR to add to the lib, just need a place to put the code until it got reviewed/accepted

p-himik 2025-06-24T12:24:12.537449Z

Ah, then src/foo/new_stuff.clj could actually make sense, but only if you're quite careful with removing it once the new stuff is merged in and the new version of foo is used by the project. Might even add a temporary test that checks that if that file is present then the specified version of foo must be exactly x.y.z.

Henry 2025-06-24T12:25:50.235029Z

good, this confirms my assumptions (coming from Ruby, this is how I would typically do a monkey patch)

gaverhae 2025-06-24T13:16:27.724149Z

When looking for a namespace full.path.to.namepsace, Clojure (the JVM, really), will look for a file at the relative path full/path/to/namespace.clj. Relative to what? The JVM process has a "classpath", which is a series of paths (typically one per dependency you have); it will look through each of these paths for the relative path above, and stop at the first one it finds. Another way to think about it is that all of your dependencies are zip files that all get extracted into your src folder, with no overwrite. This is why things are a lot simpler when each library "owns" its own prefix: if you have no common paths between libraries (and your own code), then the order of the classpath does not matter.

➕ 2
2025-06-24T15:45:46.482529Z

yeah, if your org is company and your project is myproject, the best place for your extension to bar/foo is src/company/foo/feature.clj - that's precisely the use case for having an org part and a project part

VardriPoise 2025-06-24T16:26:31.439129Z

I've added a clojure.spec.alpha/assert to a lower-level function and now I've got an assertion failure somewhere in my code, which I don't think I'll be able to find precisely without progressively commenting stuff until I eventually reach the point where I comment out the code causing the failure. Is there a more efficient way to go about finding precisely where this assertion failure occurs?

2025-06-24T16:28:50.021909Z

It should tell you in the exception trace. Your editor should also let you jump directly to it.

2025-06-24T16:29:16.863759Z

Some editors sometimes hide the details of the exception, something that trips up beginners a lot.

VardriPoise 2025-06-24T16:32:36.648609Z

Ah I see, I'll look this up ty!

2025-06-24T18:50:05.984729Z

is there any function to let binding a symbol to itself? (let [foo foo] ...)

gaverhae 2025-06-30T07:48:18.537479Z

Getting back to the original question, which was not about type hints: when you write (let [foo foo] ...) you are creating a new local binding called foo that shadows the existing, external binding of foo. The order of operation for let is always first evaluate completely the right-part, then create the binding defined by the left-part. So you get a new, local binding of the symbol foo to the value that the (external) symbol foo was mapped to. In the vast majority of cases this is useless, and it only works if foo was already declared. I can see two reasons to do that off the top of my head: • The external foo is a dynamic var and you want to get a stable reference to its current value. I'd personally advise you to steer away from dynamic vars, but to each their own I guess. • You somehow believe that a local is "accessed" faster than a var. I have done quite a bit of optimization work in Clojure and never thought to try that, so I can't completely dismiss it, but I would be surprised if this mattered.

dpsutton 2025-06-24T18:57:44.286649Z

are you asking why something might do this?

dpsutton 2025-06-24T18:57:48.615409Z

common pattern in macros

dpsutton 2025-06-24T18:58:13.800319Z

or a pattern similar (let [arg# ~arg] …)

2025-06-24T18:59:33.123629Z

No, not the macro pattern, cuz those are meaningfully different. I'm thinking of lines like this: https://github.com/clj-kondo/clj-kondo/blob/0f55a04562f28a835c520faa7cc3ca7ba0b2aa77/src/clj_kondo/impl/macroexpand.clj#L18

2025-06-24T19:01:21.589279Z

Doing reassignment with a reader conditional or with a type-hint makes sense:

(let [foo #?(:clj foo :cljs (js-foo-getter))] ...)

(let [foo ^ArrayList foo] ...)
but sometimes, i see code that (probably through renaming) ends up with a (let [... foo foo ...] ...) construction, and i'm wondering if there's special significance to it (like it helps the compiler) or if it's merely a no-op

dpsutton 2025-06-24T19:02:46.095029Z

i’d be surprised if it isn’t more a quirk of authors just listing stuff in one place and not splitting locals from arguments

👍 1
2025-06-24T19:05:51.583589Z

(let [^ArrayList foo foo] ...) is the correct type hint placement

2025-06-24T19:09:28.772609Z

😕

2025-06-24T19:10:35.481859Z

that code is 15 years old, so it's more than likely that the correct placement has changed since then, but it's funny to see old/discouraged type hints in core

2025-06-24T19:11:18.677769Z

no it hasn't changed, it just also happens to work the other way

dpsutton 2025-06-24T19:11:27.261599Z

it would propagate there right? that’s a bit different and still correct? (let [sum ^Long (+ 1 1)] … is different from (let [sum ^Long sum]…)

2025-06-24T19:11:34.806279Z

i mean "preferred", of course

2025-06-24T19:13:12.950279Z

same way that clojure.core is peppered with functions with type hints on the symbol and not the param vector

2025-06-24T19:16:07.260169Z

different, the type hinting the arg vector is a new syntax that has become preferred, but the type hint on the var pre-dates it and used to be the only way to do it (and for non fns it is still how you type hint vars)

2025-06-24T19:17:14.509909Z

for lets both locations for type hints have always "worked" but hinting the new name is correct (for some value of correct which is tricky to nail down because there is no language spec, and the impl as mentioned accepts both)

💡 2