This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
- # aleph (19)
- # aws (1)
- # beginners (75)
- # boot (28)
- # cider (1)
- # cljs-dev (12)
- # cljsrn (20)
- # clojure (350)
- # clojure-argentina (1)
- # clojure-chicago (2)
- # clojure-dev (2)
- # clojure-russia (5)
- # clojure-spec (2)
- # clojure-uk (14)
- # clojure-ukraine (3)
- # clojurescript (68)
- # component (87)
- # core-async (25)
- # core-logic (13)
- # cursive (4)
- # data-science (72)
- # datascript (59)
- # datomic (15)
- # defnpodcast (7)
- # emacs (33)
- # hoplon (5)
- # immutant (73)
- # jobs (21)
- # klipse (6)
- # lumo (14)
- # off-topic (26)
- # om (23)
- # onyx (6)
- # parinfer (37)
- # protorepl (4)
- # re-frame (13)
- # ring (2)
- # rum (3)
- # spacemacs (2)
- # specter (22)
- # sql (47)
- # uncomplicate (10)
- # unrepl (79)
- # untangled (66)
- # vim (47)
- # yada (17)
I've known about Component for a while and thought I finally have a project where it makes sense to use it, but after digging into it a bit more I'm leaning towards implementing my own solution.
Mainly I noticed this line in the README: > For small applications, declaring the dependency relationships among components may actually be more work than manually starting all the components in the correct order. You can still use the 'Lifecycle' protocol without using the dependency-injection features, but the added value of 'component' in that case is small.
What interested me as I'm trying to make this library more modular was not having the atomic map I use as a store global and having a constructor to initialize it. A dependency graph of my functions wouldn't be valuable in this case, so it seems to make more sense to just have a macro called from main that creates the atomic map, populates it, and implicitly passes the var to whatever functions are going to be called at the beginning of the chain.
I'd be interested to get anyone familiar with Component's opinion before I abandon it and go that route, i.e. if there's anything it could offer me that would make it worthwhile to use without the dependency graph.
the more deconstructed your application is, the more useful it is going to be in stitching it together
the other incredibly useful feature of component is your system is an anonymous value, it has no name unless you give it one, you can create multiple instances of your systems in a collection and map over them, or run tests against different instances in parallel, etc
if you are doing stuff with vars you are already dealing with global singletons and have thrown away all the benefits of values
I'm not sure what you mean by "deconstructed," but the functions in this library call each other in a fairly deterministic manner. The reason it made me think of Component was because they all pull and push data to a global atomic map. So rather than passing data between themselves they pass a reference to the map, sort of like handrolled continuations and for a similar purpose: because I needed to heavily alter the control flow.
My understanding is that components are a way to wrap applications that use this pattern so as to avoid using a global map.
But since it's a library and there will be only one global map, I figure I can just encapsulate it in the namespace and use a macro as the constructor called by whatever namespace is using it.
if you want to structure an application around in memory global state (which I think is a bad idead to start with), I don't think component is good way to do it
As mentioned, my understanding (largely from watching Stuart's talk on them a few years back) is that they provide an alternative to using a global map.
people tend to seem to have a hard idea coming to grips with what component actually does, so I have this little demo https://gist.github.com/hiredman/075b45eaeb01e4b526ce6f8854685487 which has all the functionality of component, just not as nicely packaged
it provides an alternative in the "doing something else" sense, not the "makes it easier to structure applications around" sense
I think the difference is in this case I can't pass a map around, I need it to be an atom that all my functions refer to. Components seem to fit the former model better.
but something like at atom is
(def x ...) and then mutated, that state is held in a component, so to mutate the atom you need to depend on that component, and that component has start and stop methods to setup or wipe the state as needed
I would strongly recommend you write functional well scoped code 🙂 in which case component will work great
And I'm understanding now the benefits you get form defining a dependency graph in that, as you mentioned, you can store it in a collection and operate on it that way. But that wouldn't be of use to me with this library. I'm interested in making it a component for the lifecycle management and avoidance of global state.
But in this case I can't make everything functionally scoped so it seems the patterns are incompatible. The reason I'm passing a reference to a global atom instead of a map itself is because I need to transform the control flow between function calls I chain together.
if you have function F, and function G, and they both fiddle with a global def A, there is a dependency there, component just makes it more explicit
if you are already passing a reference to a global map, why not pass a reference to a local map?
sure, and passing functions what they need instead of refering to a global named value
Right. that's what I'm interested in. An alternative to using namespaces for encapsulation and having to pass the reference.
when I start a new project, if it is a library, I want to parameterize it, make it possible for users to pass in whatever values as needed
But I wouldn't get any use out of a dependency graph since the map isn't being passed around. The reference to it is just static.
when I write a server of some kind, that is when I use component to fill in the parameters basically
Yes, and I'm saying I could parameterize it on the namespace level and just use a macro as the constructor. That's the decision I'm trying to make.
Considering I don't need the dependency graph, which seems to be a huge draw of using components.
right, and what I am saying is, that global reference goes against the grain, and I think defeats the purpose of using component
The deal you make with component is that in order to access stateful parts of a lib you express it as a dependency, then you access the stateful parts via your component, they are passed in when your start method is called.
Basically that defining the dependency graph is necessary to avoid passing around a reference to the state.
like many other architectural constructs, it tends to be contagious - to use a component you should implement a component etc.
I guess I'm trying to evaluate the value I'd get from that given I'm just discussing one small library in its own namespace. If it were just a part of the library then I wouldn't have the option of using global state and ns level encapsulation.
for a library I am not sure if it is valuable, I've only used it for constructing applications
And applications calling the library definitely do not need access to its dependency structure. That might be the crux of my decision?
I’ve made libraries that implement a component but no dependency graph, so that an application can instantiate it and access its state within its own graph
But if I'm not planning on calling it from a component you seem to be saying it's not worth it?
eg. I know two parts of my own cluster both need to set up kafka in a certain way, making a lib that defines that component is useful - because a given server might want to act on N kafka channels and needs to know they are initialized and restartable etc.
if I call the constructor twice with two different states, I expect to get two different things to fiddle with
What's lacking with the "namespaces as modules" pattern is an ability to have multiple instances of them
And the README seems to say one can absolutely use it just for the lifecycle methods. I think @noisesmith touched on what that would look like with only shared state in the graph
Still debating...the cost of refactoring seems a bit large, but I don't seen another way to get multiple instances of the library running
Especially since I don't ever plan on integrating this one potential component into a "system." It would literally just be a way to be able to construct multiple instances of it from the client.