Hey, interesting approach but I don't like the nesting and "manual" wiring of dependencies. I don't quite like that every with-* function remains on the stack as well, but it shouldn't hurt that much. An uncaught exception will also take down your entire system, but I guess you'd have a try/catch in your "latch" anyways.
But what I miss the most is an instance of your "app" (ie. all components together). You create it yourself in the example but I really want that always. Sometimes you just want to access your system from the outside just to see whats up (eg. REPL into a live system). I also consider the webserver to be a "client" of my "app" and not part of it (or another layer of it if you will), but that is a topic for another day. Way way back in the day I used to work with (and on) PicoContainer which was/is a dependency injection and lifecycle management container. I tried writing a DSL for it (in Groovy, this was 2003 or so) but concluded that Java already was good enough to set everything up, a DSL (or XML) is overkill. All you need to describe a "Component" is: a) what are its dependencies b) how do I start it c) how do I stop it In that light I wrote my own "dependency injection" helper functions since nothing like Stuart's Component existed at the time I got into Clojure. I don't like Component due to its invasive protocol but in essence I do the same. In my system I just set up a map of components and use that as a descriptor for wiring: {:a {:depends-on [] :start my.components.a/start :stop my.components.a/stop} :b {:depends-on [:a] :start my.components.b/start :stop my.components.b/stop}} The key in the outer map becomes whatever the :start function returns and is refered to it by its name :a (the key of the map). The :start function of :b is called as (my.components.b/start instance-of-a). An instance of a component is treated as an opaque value and other components interact with it only via its "public" interface (ie. my.components.a). Whether this is done via a protocol or not doesn't matter. When a shutdown is requested the :stop function is called with the instance of the component as the argument. That is about it. Mocking is just assoc over default descriptor map and I have helper functions to only do partial start/stop calls if only a specific component is needed (eg. I only need :a). Like I said it basically does the same stuff as Component, just a little less invasive since I think a component should not know about the container it runs in. Hope that was somewhat useful as feedback to Yo-Yo. Cheers, /thomas On Sunday, June 28, 2015 at 4:03:34 PM UTC+2, James Henderson wrote: > > As promised, have blogged: 'Yo-yo & Component - Side by Side > <https://github.com/james-henderson/yoyo/blob/master/articles/side-by-side.org> > ' > > Contents: > > > - Making components > > <https://github.com/james-henderson/yoyo/blob/master/articles/side-by-side.org#making-components> > - Using a component as a dependency > > <https://github.com/james-henderson/yoyo/blob/master/articles/side-by-side.org#using-a-component-as-a-dependency> > - Serving a REST API > > <https://github.com/james-henderson/yoyo/blob/master/articles/side-by-side.org#serving-a-rest-api> > - Wiring it all up > > <https://github.com/james-henderson/yoyo/blob/master/articles/side-by-side.org#wiring-it-all-up> > - Yo-yo / Component Interoperability > > <https://github.com/james-henderson/yoyo/blob/master/articles/side-by-side.org#yo-yocomponent-interoperability> > - Mockable Services > > <https://github.com/james-henderson/yoyo/blob/master/articles/side-by-side.org#mockable-services> > - ‘Mocking out’ dependencies > > <https://github.com/james-henderson/yoyo/blob/master/articles/mocking-out-dependencies> > > Let me know what you think! > > Cheers, > > James > > On Thursday, 25 June 2015 09:25:56 UTC+1, James Henderson wrote: >> >> Seems like the next step for this would be for me to put together a blog >> with an example Component system, and its equivalent Yoyo system?! :) >> Should have time for that over the weekend. >> >> James >> >> On Thursday, 25 June 2015 09:05:39 UTC+1, James Henderson wrote: >>> >>> >>> >>> On Wednesday, 24 June 2015 11:17:41 UTC+1, Atamert Ölçgen wrote: >>>> >>>> >>>> >>>> On Tue, Jun 23, 2015 at 11:47 PM, James Henderson <ja...@jarohen.me.uk> >>>> wrote: >>>> >>>>> Hi Atamert - thanks :) >>>>> >>>>> I thought it might be preferable to keep the call to (latch)explicit >>>>> - it means that ylet can be used in nested calls, too - for example, >>>>> to set up and compose groups of components/sub-systems: (contrived >>>>> example, >>>>> though!) >>>>> >>>>> ;; (docs for ylet at >>>>> https://github.com/james-henderson/yoyo#introducing-ylet ) >>>>> >>>>> (require '[yoyo :refer [ylet]]) >>>>> >>>>> (defn with-connections [config f] >>>>> (ylet [db-pool (with-db-pool (:db config)) >>>>> es-conn (with-es-connection (:elasticsearch config))] >>>>> >>>>> (f {:db-pool db-pool >>>>> :es-conn es-conn}))) >>>>> >>>>> (defn make-system [latch] >>>>> (let [config ...] >>>>> (ylet [connections (with-connections system) >>>>> _ (with-webserver {:handler (make-handler (merge connections >>>>> {:config >>>>> config})) >>>>> :port 3000})] >>>>> (latch)))) >>>>> >>>>> >>>>> How would you see the with-* functions working, btw? >>>>> >>>> >>>> I think the general idea should be to provide a clean API to the >>>> consumer (of your lib). Perhaps something that accepts a start function, a >>>> stop function and some sort of main loop (f in your example). >>>> >>> >>> Not sure I understand what you mean here? Tbh, I was trying to get away >>> from the idea of separate start & stop functions - it seems 'cleaner' to me >>> without them! (although of course that's subjective). >>> >>> Also, the 'with-*' functions here are consumer code - the only Yo-yo >>> functions/macros in this example are 'run-system!' and 'ylet'. Yo-yo itself >>> is *tiny* (<100 LoC) - my aim was for a library that solely dealt with >>> starting/stopping a provided system, and *no more* :) >>> >>> Maybe it'd be worth fleshing out an example of what you were looking for? >>> >>> Cheers, >>> >>> James >>> >>> >>>> >>>>> >>>>> Cheers, >>>>> >>>>> James >>>>> >>>>> On Tuesday, 23 June 2015 09:57:16 UTC+1, Atamert Ölçgen wrote: >>>>>> >>>>>> Hi James, >>>>>> >>>>>> Interesting idea. Thanks for sharing. >>>>>> >>>>>> I think you can simplify this: >>>>>> >>>>>> (yoyo/run-system! >>>>>> (fn [latch] >>>>>> (ylet [db-pool (with-db-pool {...}) >>>>>> :let [server-opts {:handler (make-handler {:db-pool >>>>>> db-pool}) >>>>>> :port 3000}] >>>>>> web-server (with-web-server server-opts)] >>>>>> (do-this web-server) >>>>>> (do-that db-pool web-server) >>>>>> (latch)))) >>>>>> >>>>>> >>>>>> to: >>>>>> >>>>>> (yoyo/foo! [db-pool (with-db-pool {...}) >>>>>> :let [server-opts {:handler (make-handler {:db-pool >>>>>> db-pool}) >>>>>> :port 3000}] >>>>>> web-server (with-web-server server-opts)] >>>>>> (do-this web-server) >>>>>> (do-that db-pool web-server)) >>>>>> >>>>>> >>>>>> I believe with-* function can also be simplified further. >>>>>> >>>>>> >>>>>> On Tue, Jun 23, 2015 at 1:18 AM, James Henderson <ja...@jarohen.me.uk >>>>>> > wrote: >>>>>> >>>>>>> Hi all, >>>>>>> >>>>>>> I've just released an early version of 'Yo-yo', a protocol-less, >>>>>>> function composition-based alternative to Component. It's still in its >>>>>>> early stages, so feedback would be very much appreciated! >>>>>>> >>>>>>> https://github.com/james-henderson/yoyo >>>>>>> >>>>>>> Yo-yo was also an experiment to see what could be de-coupled from >>>>>>> the concept of 'reloadable systems', so you won't find any >>>>>>> configuration, >>>>>>> dependency injection, etc - just a way to write a system that can be >>>>>>> easily >>>>>>> started, stopped, and reloaded. >>>>>>> >>>>>>> Fundamentally, we start by assuming there's a function available >>>>>>> that only returns 'when the system stops' - a 'latch', say. If we had >>>>>>> such >>>>>>> a function, we could start our system, call that function, then stop >>>>>>> the >>>>>>> system (closing any necessary resources). A database pool, for example, >>>>>>> might look like this: >>>>>>> >>>>>>> (defn with-db-pool [db-config f] >>>>>>> (let [db-pool (start-pool! db-config)] >>>>>>> (try >>>>>>> (f db-pool) >>>>>>> >>>>>>> (finally >>>>>>> (stop-pool! db-pool))))) >>>>>>> >>>>>>> Here, we're assuming that we'll be passed 'f', the 'latch' >>>>>>> function. A web server would be similar, and, because they're both >>>>>>> functions, they're very simple to compose: >>>>>>> >>>>>>> (with-db-pool {...} >>>>>>> (fn [db-pool] >>>>>>> (with-web-server {:handler (make-handler {:db-pool db-pool}) >>>>>>> :port ...} >>>>>>> (fn [web-server] >>>>>>> ;; TODO: Ah. We've run out of turtles. :( >>>>>>> )))) >>>>>>> >>>>>>> This is where Yo-yo comes in - there’s a function called run-system!, >>>>>>> which takes a function that accepts a latch: >>>>>>> >>>>>>> (:require [yoyo]) >>>>>>> >>>>>>> (yoyo/run-system! >>>>>>> (fn [latch] >>>>>>> (with-db-pool {...} >>>>>>> (fn [db-pool] >>>>>>> (with-web-server {:handler (make-handler {:db-pool db-pool}) ; >>>>>>> n.b. we have access to the db-pool here - no need for global state! >>>>>>> :port ...} >>>>>>> (fn [web-server] >>>>>>> (latch))))))) ; Aha! >>>>>>> >>>>>>> run-system! then returns a promise - deliver any value to it, and >>>>>>> it'll stop the system. >>>>>>> >>>>>>> And that's pretty much it! There are a few more functions - mostly >>>>>>> to do with easily starting/stopping/reloading a system through the >>>>>>> REPL, >>>>>>> and a macro to simplify the 'function staircase' - these are covered in >>>>>>> more detail in the README. There are some also common components - a >>>>>>> database pool, a web server, and a simple integration for existing >>>>>>> Component systems. >>>>>>> >>>>>>> It'd be great to hear your thoughts/ideas, whatever they may be - >>>>>>> either through here, e-mail, Github, or Twitter - thanks! >>>>>>> >>>>>>> James >>>>>>> >>>>>>> -- >>>>>>> You received this message because you are subscribed to the Google >>>>>>> Groups "Clojure" group. >>>>>>> To post to this group, send email to clo...@googlegroups.com >>>>>>> Note that posts from new members are moderated - please be patient >>>>>>> with your first post. >>>>>>> To unsubscribe from this group, send email to >>>>>>> clojure+u...@googlegroups.com >>>>>>> For more options, visit this group at >>>>>>> http://groups.google.com/group/clojure?hl=en >>>>>>> --- >>>>>>> You received this message because you are subscribed to the Google >>>>>>> Groups "Clojure" group. >>>>>>> To unsubscribe from this group and stop receiving emails from it, >>>>>>> send an email to clojure+u...@googlegroups.com. >>>>>>> For more options, visit https://groups.google.com/d/optout. >>>>>>> >>>>>> >>>>>> >>>>>> >>>>>> -- >>>>>> Kind Regards, >>>>>> Atamert Ölçgen >>>>>> >>>>>> ◻◼◻ >>>>>> ◻◻◼ >>>>>> ◼◼◼ >>>>>> >>>>>> www.muhuk.com >>>>>> >>>>> -- >>>>> You received this message because you are subscribed to the Google >>>>> Groups "Clojure" group. >>>>> To post to this group, send email to clo...@googlegroups.com >>>>> Note that posts from new members are moderated - please be patient >>>>> with your first post. >>>>> To unsubscribe from this group, send email to >>>>> clojure+u...@googlegroups.com >>>>> For more options, visit this group at >>>>> http://groups.google.com/group/clojure?hl=en >>>>> --- >>>>> You received this message because you are subscribed to the Google >>>>> Groups "Clojure" group. >>>>> To unsubscribe from this group and stop receiving emails from it, send >>>>> an email to clojure+u...@googlegroups.com. >>>>> For more options, visit https://groups.google.com/d/optout. >>>>> >>>> >>>> >>>> >>>> -- >>>> Kind Regards, >>>> Atamert Ölçgen >>>> >>>> ◻◼◻ >>>> ◻◻◼ >>>> ◼◼◼ >>>> >>>> www.muhuk.com >>>> >>> -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.