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.

Reply via email to