component

sophiago 2017-06-07T20:00:42.274602Z

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.

sophiago 2017-06-07T20:00:47.276559Z

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.

sophiago 2017-06-07T20:04:46.362037Z

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.

sophiago 2017-06-07T20:06:01.388566Z

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.

2017-06-07T20:06:48.404316Z

the more deconstructed your application is, the more useful it is going to be in stitching it together

2017-06-07T20:08:00.428321Z

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

sophiago 2017-06-07T20:08:29.438229Z

Oh, that last part is very interesting

sophiago 2017-06-07T20:08:39.441627Z

Although I'm not sure useful in this case

2017-06-07T20:09:09.451990Z

if you are doing stuff with vars you are already dealing with global singletons and have thrown away all the benefits of values

sophiago 2017-06-07T20:11:13.493439Z

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.

2017-06-07T20:12:02.510099Z

a global atom is not component at all

sophiago 2017-06-07T20:12:44.524523Z

My understanding is that components are a way to wrap applications that use this pattern so as to avoid using a global map.

sophiago 2017-06-07T20:13:13.534225Z

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.

2017-06-07T20:13:38.542587Z

you may just want a graph library and a toposort function

sophiago 2017-06-07T20:14:11.553338Z

I think that's the part of what components do that I don't want 🙂

sophiago 2017-06-07T20:14:22.557338Z

Hence why I'm asking if they're even worth using in this case

2017-06-07T20:14:32.560676Z

I doubt they are

2017-06-07T20:15:03.570594Z

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

sophiago 2017-06-07T20:16:03.591274Z

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.

2017-06-07T20:16:38.603357Z

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

2017-06-07T20:16:55.609216Z

the alternative they provide is to not use a global map

2017-06-07T20:17:17.616528Z

the state lives in the components that make up the system, not def'ed in vars

2017-06-07T20:17:49.627358Z

it provides an alternative in the "doing something else" sense, not the "makes it easier to structure applications around" sense

2017-06-07T20:18:17.636587Z

so if you want an alternative to using a global map, you can do that with component

sophiago 2017-06-07T20:18:42.644751Z

So you mean every instance of shared state would constitute its own component?

2017-06-07T20:18:51.647937Z

it depends

sophiago 2017-06-07T20:19:34.661990Z

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.

2017-06-07T20:19:55.669213Z

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

2017-06-07T20:20:22.678784Z

component is functional and well scoped

sophiago 2017-06-07T20:20:24.679716Z

Right, so that was the main feature I found appealing.

2017-06-07T20:21:18.698015Z

I would strongly recommend you write functional well scoped code 🙂 in which case component will work great

sophiago 2017-06-07T20:22:14.717806Z

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.

2017-06-07T20:23:06.736074Z

you need to either directly wire stuff together, or have the dependency graph

sophiago 2017-06-07T20:23:27.743153Z

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.

2017-06-07T20:24:02.755106Z

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

sophiago 2017-06-07T20:24:26.763746Z

And it would encapsulate the global def?

2017-06-07T20:24:32.765641Z

if you are already passing a reference to a global map, why not pass a reference to a local map?

2017-06-07T20:24:40.768556Z

no

2017-06-07T20:24:46.770401Z

you get rid of the global def

sophiago 2017-06-07T20:25:03.776409Z

By wrapping the entire library in a component?

2017-06-07T20:25:35.787836Z

sure, and passing functions what they need instead of refering to a global named value

sophiago 2017-06-07T20:26:19.803362Z

Right. that's what I'm interested in. An alternative to using namespaces for encapsulation and having to pass the reference.

2017-06-07T20:26:50.813759Z

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

sophiago 2017-06-07T20:26:56.816023Z

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.

2017-06-07T20:27:17.823303Z

when I write a server of some kind, that is when I use component to fill in the parameters basically

sophiago 2017-06-07T20:27:47.834006Z

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.

sophiago 2017-06-07T20:28:02.839417Z

Considering I don't need the dependency graph, which seems to be a huge draw of using components.

2017-06-07T20:28:16.844657Z

right, and what I am saying is, that global reference goes against the grain, and I think defeats the purpose of using component

2017-06-07T20:28:25.847582Z

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.

2017-06-07T20:28:50.856082Z

you could define a component with no deps, that someone else could access

sophiago 2017-06-07T20:28:52.856877Z

@noisesmith that's a helpful explanation. Thanks.

sophiago 2017-06-07T20:29:31.869983Z

Basically that defining the dependency graph is necessary to avoid passing around a reference to the state.

2017-06-07T20:29:57.879162Z

like many other architectural constructs, it tends to be contagious - to use a component you should implement a component etc.

2017-06-07T20:30:07.882850Z

you still pass it around, the graph just defines where you get it intially

2017-06-07T20:30:09.883390Z

well, a client needs a graph, you could just provide a component

2017-06-07T20:30:29.891059Z

(depending on the scope of your own lib of course)

sophiago 2017-06-07T20:30:59.902096Z

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.

2017-06-07T20:31:40.916747Z

for a library I am not sure if it is valuable, I've only used it for constructing applications

sophiago 2017-06-07T20:31:41.917242Z

And applications calling the library definitely do not need access to its dependency structure. That might be the crux of my decision?

sophiago 2017-06-07T20:31:56.922393Z

@hiredman I think you're onto something there

2017-06-07T20:32:21.931223Z

some libraries are more like embedded applications though

2017-06-07T20:33:01.945308Z

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

sophiago 2017-06-07T20:33:29.955104Z

But if I'm not planning on calling it from a component you seem to be saying it's not worth it?

2017-06-07T20:33:43.960129Z

it depends

2017-06-07T20:33:56.964360Z

I definitely would not stick a global singleton in a library

2017-06-07T20:34:05.967095Z

those are the worst libraries

donaldball 2017-06-07T20:34:17.971327Z

cosign

sophiago 2017-06-07T20:34:20.972285Z

Is there nothing to say for the ability to use namespaces as modules?

2017-06-07T20:34:31.975819Z

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.

2017-06-07T20:34:45.980945Z

well, if a namespace is a module I wouldn’t want any globals that hold state

2017-06-07T20:35:03.987300Z

since we can’t simply make a fresh copy of a given namespace

sophiago 2017-06-07T20:35:06.988242Z

What if all the state is passed in through a constructor?

2017-06-07T20:35:19.992782Z

as long as it isn't global

2017-06-07T20:35:44.001657Z

if I call the constructor twice with two different states, I expect to get two different things to fiddle with

sophiago 2017-06-07T20:35:59.006410Z

Ah I see. That's what I wouldn't get rolling my own solution

sophiago 2017-06-07T20:36:34.018595Z

What's lacking with the "namespaces as modules" pattern is an ability to have multiple instances of them

2017-06-07T20:36:50.023854Z

namespaces are not modules, so there is a lot lacking in that pattern

2017-06-07T20:37:07.029581Z

(depending on which langauge's concepts of modules you are coming in with)

sophiago 2017-06-07T20:37:09.030242Z

To be clear, I definitely can't require an ns twice with different aliases, right?

2017-06-07T20:37:24.035664Z

no, namespaces are not parameterized

sophiago 2017-06-07T20:37:31.038060Z

Or rather I can, but it wouldn't do what I'm imagining...

sophiago 2017-06-07T20:37:48.043941Z

Ok, so that's the functionality I'd be looking for along with a constructor

sophiago 2017-06-07T20:38:10.051147Z

Or rather the tradeoff I'd be making if I don't use Component

sophiago 2017-06-07T20:39:29.077855Z

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

sophiago 2017-06-07T20:42:53.146558Z

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

sophiago 2017-06-07T20:45:15.194482Z

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.

sophiago 2017-06-07T20:48:11.253688Z

I think my conclusion is making this a component doesn't add much more value than just refactoring it to pass around a map (if possible) rather than using a def, which would be a prerequisite anyway.