clojure

New to Clojure? Try the #beginners channel. Official docs: https://clojure.org/ Searchable message archives: https://clojurians-log.clojureverse.org/
zendevil 2020-12-06T03:48:33.082700Z

Is there a point cloud library or an open 3d alternative for clojure?

seancorfield 2020-12-06T04:36:59.082800Z

We strongly discourage posting the same question in multiple channels @ps

✅ 1
william 2020-12-06T16:00:23.089800Z

hey! I'm refactoring the state of an application, and I'd like to state an invariant that has to be checked every time some operation is done on the state. I think spec is the right tool, but if I understand correctly, instrument is only used to instrument functions, right?

MKLUO 2020-12-06T16:26:48.089900Z

I don't think it is what spec is for. Since the invariants is usually a part of your domain logic, explicitly asserting/checking it should be the way to go.

jfntn 2020-12-06T16:43:37.092100Z

What’s a good way to downgrade the jdk installed by the homebrew clojure formula?

2020-12-06T16:56:02.092200Z

If I understand correctly, only a formerly-but-no-longer-supported version of the homebrew clojure formula installs a particular version of the jdk. If you use the latest officially released version of the homebrew Clojure formula, it will not install any version of the jdk for you, and you can pick any jdk you want: brew install clojure/tools/clojure as stated on https://clojure.org/guides/getting_started

2020-12-06T16:56:35.092400Z

So starting with brew uninstall clojure and removing the brew-installed jdk you do not want is probably a good start.

Gleb Posobin 2020-12-06T17:24:58.096200Z

stuartsierra/component's [readme](https://github.com/stuartsierra/component) says "No function should take the entire system as an argument ... If a function depends on several components, then it should have its own component with dependencies on the things it needs." What's the reasoning for that? That reminds me of OOP a lot, where you would make a new class just to pass in structured data to a couple of functions or something like that. Why is passing the system and destructuring is not ok, like (defn f [{:keys [db task-queue]} ...] ...), and calling (f system ...) ? He says "This is unnecessary sharing of global state.", but I don't see how that is, if each function takes only what it and the function it calls needs.

2020-12-07T17:27:01.170Z

regarding routes using components, my solution has been for each component to make a wrap-foo function, and to have the routing code extract and use those, rather than passing the entirety of the component to every request handler

2020-12-07T17:27:29.170200Z

(the wrap-foo function is provided as a key in the component map)

Gleb Posobin 2020-12-07T17:58:39.170500Z

@noisesmith what does wrap-foo do?

2020-12-07T18:00:14.170700Z

it's a middleware that offers some stateful resource to the handler - eg.

(fn wrap-connection [handler]
  (fn [request]
    (handler (assoc request :db-connection conn))))

2020-12-07T18:01:07.170900Z

so the implementation detail that the connection comes from a component is invisible to the handler, which finds some resource under a key, as it would with any other middleware enabled feature

2020-12-07T18:01:49.171100Z

this means changing how state is managed doesn't require refactoring request-handling code

2020-12-07T18:03:33.171300Z

pragmatically, one of the biggest drawbacks of passing your mega state-blob everywhere is that you need to edit your entire codebase if you need to restructure that blob, this reduces the scope of that problem

2020-12-07T18:05:03.171700Z

which is part of what Stuart Sierra is getting at in your original link

2020-12-07T18:13:41.171900Z

one way to interpret "structured programming" is to see it as a series of firewalls which minimize the number of changes needed when you add features or change implementation; putting all your resources into a single hash-map that every function sees is effectively an unstructuring, spreading implementing dependencies through the cracks of your structure

seancorfield 2020-12-07T18:42:58.173800Z

@noisesmith Interesting approach. So you have explicit middleware invocations around handlers directly in your routes definitions? Doesn't that get ugly with dozens of components in play? (or do you only have a few components in your systems?)

2020-12-07T18:44:34.174900Z

@seancorfield this was done with an interfacing function that consumes all the wrap-foo functions and composes them in the desired order, returning a composed middleware for the handlers

2020-12-07T18:44:58.175200Z

so no, it's not invoked at the point of the handlers

seancorfield 2020-12-07T18:54:39.186Z

Ah, so all handlers have access to all of the (relevant) system component parts separately but not the whole system component? Effectively "unrolling" the compound system to place just the lower-level pieces into the Ring request -- in our case that would go from having the "Application" in the request, where handlers would need to unpack it to get at :database and then :pooled-db or whatever nested resource, to the Ring request having those nested resources directly available under keys in the request?

seancorfield 2020-12-07T18:55:34.186200Z

Thus removing knowledge of how the System is structured.

2020-12-07T18:55:59.186400Z

precisely - nothing stops using namespaced keys / assoc-in instead of assoc for those resources

2020-12-07T18:56:19.186600Z

but the key is that it's not based on knowledge of the system's structure

seancorfield 2020-12-07T18:57:20.186800Z

I don't think we've ever found the need to restructure our system-level component in a way that would break functions down the call chain but I can see the benefit of "flattening" the component into the request.

2020-12-07T18:58:32.187Z

it might be that our benefit was partially because we had "weird" components which exposed some implementation detail of external service usage etc., so we did have restructuring of our component map as our architecture evolved

2020-12-07T18:59:09.187200Z

and the bottleneck of providing things through functions in middleware helped keep things to an interface

seancorfield 2020-12-07T19:28:18.187400Z

Ah, that makes sense then. Yeah, I can definitely see the benefit in decoupling this. I'll bear it in mind for the new web app I build 🙂

jfntn 2020-12-06T17:28:38.096900Z

Thanks Andy, that tap was key, somehow brew install clojure is hardwired to install jdk 15?

2020-12-06T17:29:27.097100Z

Yes. There was a change made, maybe about a year ago?, by some Homebrew maintainers to cause many projects that depend upon a JDK, to depend upon a recent version of the JDK.

2020-12-06T17:30:10.097300Z

Clojure devs attempted to persuade them not to do so for Clojure, but they did not agree, so a separate Homebrew project was created for Clojure that they apparently allow to have control over that.

mike_ananev 2020-12-06T17:45:14.098800Z

@posobin Component library has very OOP-ish approach. Try another libraries like mount https://github.com/tolitius/mount or context https://github.com/redstarssystems/context

Gleb Posobin 2020-12-06T17:45:58.099400Z

Well I am already using component =)

Gleb Posobin 2020-12-06T17:46:44.100700Z

I saw @seancorfield say that mount complects things, and I am starting to agree with him after using it for a while.

✔️ 1
p-himik 2020-12-06T17:58:13.100800Z

Integrant is a nice one as well.

lukasz 2020-12-06T18:08:01.101100Z

It's not so much an OOP-ism (nothing wrong with that to be honest) but it's about separation of concerns - if you have a function which is only responsible for sending out emails, why would you pass database connections, slack client and a whole config map to it?

Gleb Posobin 2020-12-06T18:09:18.101300Z

Because it might require a db connection later on when I add logging of sent emails to the db?

lukasz 2020-12-06T18:09:36.101500Z

That sounds like YAGNI

lukasz 2020-12-06T18:09:49.101700Z

if you will need it later, you will add it

lukasz 2020-12-06T18:11:01.101900Z

Believe me - I have overcorrected with making everything a component in the early days, and it definitely needs some balancing.

lukasz 2020-12-06T18:12:40.102100Z

And yes, we had some things depending on the whole system only to realize it was a smell and things had to be taken apart - I think the biggest drawback was to understand the code after while and having the whole system passed in would lead to sub-optimal design decisions

lukasz 2020-12-06T18:13:12.102300Z

Basically you have a global state, but you're not admitting it because it's always the first argument of all your functions 😉

Gleb Posobin 2020-12-06T18:13:39.102500Z

But adding it is a pain, have to adjust the arity of the function in all call sites.

Gleb Posobin 2020-12-06T18:14:08.102700Z

Do you have a special email-component-bundle in your system then?

dharrigan 2020-12-06T18:14:47.103Z

My favourite is Juxt Clip

dharrigan 2020-12-06T18:15:09.103200Z

<https://github.com/juxt/clip>

dharrigan 2020-12-06T18:15:41.103900Z

I use that for managing all my "dependencies", such as connections to the db, redis, external APIs etc..

dharrigan 2020-12-06T18:16:58.104300Z

I have a small example of its usage here <https://git.sr.ht/~dharrigan/startrek/tree> with a tiny example project.

lukasz 2020-12-06T18:19:51.104400Z

You don't have to adjust function arguments - usually you pass the dependency map as the first argument, and you use destructuring to pull them out. In case of the email-sender, they way we have modeled is that the email-sender itself is a component, injected into other bits which needed (and only them)

Gleb Posobin 2020-12-06T18:24:38.104600Z

I see. One other problem I have is that there is a routing function that calls many possible functions that respond to requests, and each of those functions needs a bunch of components (some might need the db and s3, some might need access to the email and db components, etc). How do you handle that? So far the router component just depends on many other components, and passes the db s3 email etc components to the functions it calls as separate arguments. How would you structure this better?

lukasz 2020-12-06T18:28:19.104800Z

Ah yes - that's definitely one of these cases where you sort of depend on most of the system. I don't think there's a good way around it unless your individual route handlers start being components. That's an overkill IMHO

lukasz 2020-12-06T18:30:20.105Z

That said, in that case - the code is always very explicit about which components it requires, so we structure it so that top level handlers responsible for each route, pick out which components to pass down (effectively a subset of dependencies). This way, for convenience you can always call a function with the whole system (e.g. in a REPL) but you can always be sure about which components it will use as these usually get pulled out via destructuring of arrgs

lukasz 2020-12-06T18:30:39.105200Z

(damn it my macbooks keyboard is on its last legs...)

2020-12-06T18:44:55.105500Z

@posobin The only solution I’ve seen for that is to have the whole system available to the top-level route (usually tacked onto the request), which then destructures out the components it needs.

2020-12-06T18:45:28.105700Z

IME this works fine with few problems. You can still find what fn uses what components by keyword searching if you use namespaced keywords.

p-himik 2020-12-06T18:47:24.106400Z

> Project status: Alpha. The only thing that stops me from using clip.

Gleb Posobin 2020-12-06T19:02:00.106500Z

@potetm @lukaszkorecki How do you pass the components to the particular route handlers? As separate arguments or make a map from the ones that are needed and pass them? Like I am passing each component as a separate argument, and some routes didn't need the db connection previously, but now I added saving some logging to the db, and now need to pass a new argument to a bunch of functions.

2020-12-06T19:02:43.106800Z

pass the whole system map to every route handler

2020-12-06T19:02:48.107Z

is what i was suggesting

Gleb Posobin 2020-12-06T19:02:58.107200Z

Sorry, not route handler, but the function that the route handler calls.

2020-12-06T19:03:17.107400Z

really just a matter of taste

2020-12-06T19:03:31.107600Z

I usually do separate args

lukasz 2020-12-06T19:03:40.107800Z

Similar ☝️ the handler is a component, which passes down the dependencies as a key in the request map, and then each handler function for a particular route unpicks what they need and passes it down as a map

2020-12-06T19:03:44.108Z

(logging components seems like overkill to me. I use it too prolifically.)

2020-12-06T19:04:24.108200Z

But if a fn takes a bunch of components, I’ll accept a map

2020-12-06T19:06:19.108400Z

I think Stuart’s advice is a little over done. It’s helpful for a fn signature to tell what components it needs. Namespaced keywords provide similar utility.

2020-12-06T19:06:37.108600Z

(arguably namespaced keywords have better utility)

Gleb Posobin 2020-12-06T19:06:44.108800Z

Can you expand on how namespaced keywords help?

2020-12-06T19:07:03.109Z

the upside of separate args for each component is you can tell by looking what the fn might or might not do

2020-12-06T19:07:37.109200Z

if you destructure+namespace-key, you get basically the same experience

2020-12-06T19:08:09.109400Z

plus: spec support and symbol-searching support

lukasz 2020-12-06T19:08:34.109600Z

We have a rule to always pass components (even if it's just one) as a map - otherwise some functions would have 5 component args + input. A bit too much for my taste

2020-12-06T19:09:07.109800Z

The downside of passing a big glob of components everywhere is it’s impossible to tell who’s doing what. You can mitigate this by destructuring as high in the stack as you can bear.

Gleb Posobin 2020-12-06T19:09:50.110Z

I just have trouble coming up with how I would namespace components. I have the db, email, s3 components in mind.

Gleb Posobin 2020-12-06T19:10:23.110200Z

Are you speccing your system?

seancorfield 2020-12-06T19:10:44.110400Z

In most of my code that uses Component, I put the "application" component (everything in the "system" except the web server itself) into the Ring request via middleware created at startup. So every handler has access to the "application" and then it can pass just what sub-components are needed down into the "model" functions that the handler calls.

2020-12-06T19:11:25.110600Z

(component/system-map :app/db (db), :app/monitoring (monitoring))

2020-12-06T19:11:28.110800Z

should work, no?

2020-12-06T19:12:04.111Z

(I’m honestly not sure. I work in a home-rolled component system. Do not do that. Cannot recommend.)

seancorfield 2020-12-06T19:12:30.111200Z

We currently have a :database component that has several datasources nested inside it. If we were starting over, we'd probably use namespace-qualified keywords and not have them nested.

2020-12-06T19:13:48.111400Z

I’ve not spec’d my system. (It’s garbage, and cannot be spec’d.) But there’s nothing stopping you from saying :app/db must be a DataSource or smth along those lines.

2020-12-06T19:16:36.111600Z

I think worst case is you have to un-namespace those keys so that they can play w/ records (which do not support namespacing).

2020-12-06T19:16:54.111800Z

You can do that with component/using

Gleb Posobin 2020-12-06T19:18:23.112Z

Ah, the namespace is not for grouping components, but just so that you can spec them?

Gleb Posobin 2020-12-06T19:20:53.112200Z

But you can spec without namespaces too. @seancorfield's example with several datasources makes more sense.

seancorfield 2020-12-06T19:35:16.112700Z

Spec'ing is orthogonal to this.

seancorfield 2020-12-06T19:35:51.112900Z

And you don't have to use records for Component. The protocols can be implemented via metadata on anything that can support metadata.

💯 1
Gleb Posobin 2020-12-06T19:36:28.113100Z

Ooh, didn't know that latter point.

Gleb Posobin 2020-12-06T19:37:19.113300Z

@seancorfield You said "in my code that uses Component...", is Component your preferred library for state management, or do you like some other one more?

seancorfield 2020-12-06T19:39:13.113500Z

For components that have dependencies, you can use hash maps or records. For components without dependencies, you have much more leeway.

seancorfield 2020-12-06T19:39:32.113700Z

In next.jdbc.connection I have a function used as a component.

👍 1
seancorfield 2020-12-06T19:40:26.113900Z

At work, we use Component extensively. We have a monorepo with over 100K lines of Clojure, in three dozen subprojects, that are combined into just over a dozen services.

seancorfield 2020-12-06T19:44:09.114100Z

One of our apps is structured with every Ring handler being a component with its own dependencies. The rest are less granular. We have components for database connections, caching, our HTML email templating system, ...

seancorfield 2020-12-06T19:49:05.114400Z

...our environment/configuration system, our JWT-based security component, Redis pooling, Redis pub/sub, and many more. We have 43 implementations of component/Lifecycle apparently (some in test fixtures where we use mocked components). Looks like we use the metadata protocol approach for three more component implementations. @posobin

dharrigan 2020-12-06T20:07:15.115Z

most of the core clojure libraries have been alpha until very recently...

dharrigan 2020-12-06T20:07:49.115800Z

anyhooo, I've been using Juxt Clip in production, on systems running 24-7, processing millions of messages per day. Zero issues.

dharrigan 2020-12-06T20:08:22.116400Z

A testiment to how well it has been written and how well Clojure itself is put together 🙂

p-himik 2020-12-06T20:09:16.116600Z

It's not the quality that concerns me, it's the probability of backwards incompatible changes.

p-himik 2020-12-06T20:09:51.116800Z

Although, if alpha is stable enough then there might be no reason to update to a newer version that changes some API.

dharrigan 2020-12-06T20:10:18.117Z

That is always a problem with any library one uses. A breaking change at some point. One deals with it, if it arises. However, I'm encouraged by this line The core API is unlikely to change significantly, but there may be some small changes to the system-config format

kenny 2020-12-06T23:21:25.118700Z

Does anyone know of a code security scanning tool for Clojure? I imagine it to be quite tricky to do without many false positives. However, it checks a box on security checklists and seems to make certain customers happy.

denik 2020-12-06T23:36:00.119600Z

When running a REPL w/ tools.deps is there a way to access the names of the active aliases?

seancorfield 2020-12-06T23:52:56.120Z

@denik No. That information is gone by the time the process starts.

🙏 1
seancorfield 2020-12-06T23:53:18.120500Z

The aliases are used to create the classpath that in turn is used to run the REPL.

seancorfield 2020-12-06T23:53:57.121Z

@kenny I've never heard of a useful security scanning tool that works with Clojure code.

dominicm 2020-12-07T08:53:06.147200Z

Running the scan is not the same as reading it, of course :).

kenny 2020-12-06T23:56:32.121100Z

Yeah... We're trying Fortify's static scan and I imagine it's going to be completely useless. It's unfortunate that so many people consider scans like this as a base-level security metric to work with you.

seancorfield 2020-12-06T23:59:07.121300Z

I've done a huge amount of work with static source code analyzers over the decades and while there are a few constructs which can be identified in nearly all languages as being suspect, in order to "sell" a security tool, it has to be "thorough" and err on the side of false positives, which in turn makes the tools pretty much useless really.

👍 1