Thanks, Colin. Macros hadn't crossed my mind, so it's good to have them pointed out. Do you have a macro you could post that is a good example of enforcing a pattern as an implementation detail? I think that's a good general consideration, but I don't believe it fits this use case. Though an example may be more illuminating.
In some ways, this use case seems akin to the implementation of nth in ClojureScript, where it's exact behavior can differ depending on the protocols satisfied by the passed collection. Looking back through the component docs, update-system and update-system-reverse may be the key piece for implementing something like the LifecycleStatus solution in my original email without requiring any change to component itself. The big weakness is that it would require using a custom start-system stop-system function rather than the standard one. Andrew Oberstar On Sun, Mar 15, 2015 at 11:32 AM Colin Yates <colin.ya...@gmail.com> wrote: > In OO we tend to solve the 'copy and paste' problem with abstract > classes. In Clojure we also have macros, easily overused, sure, but > worth knowing about. They turn the problem on its head and allow truly > composable functionality. I am not stating they _are_ appropriate > here, only that you might want to think about them; whenever I have a > 'I want this pattern enforced, but it is really just an implementation > detail', a macro is sometimes the answer. > > On 15 March 2015 at 15:58, Andrew Oberstar <ajobers...@gmail.com> wrote: > > I'm fairly new to Clojure, so I'm still struggling to unlearn the habits > of > > OO-programming. While using Stuart Sierra's component library, I've found > > the recommendation in the docs of using idempotent lifecycles very > helpful. > > The unfortunate result is that every component then has the same pattern > in > > its start and stop methods: > > > > (defrecord SillyExample [...] > > component/Lifecycle > > (start [this] > > (if (custom-started-check? this) > > this > > (custom-start-logic this))) > > (stop [this] > > (if (custom-started-check? this) > > (custom-stop-logic this) > > this))) > > > > It adds some extra nesting and, potentially, duplication of the started > > check's logic. In hopes of making idempotent lifecycles easier to > implement, > > I made the following protocol, which seems to violate the implementation > > inheritance philosophy of Clojure. > > > > (defprotocol IdempotentLifecycle > > (started? [this]) > > (safe-start [this]) > > (safe-stop [this])) > > > > (extend-protocol component/Lifecycle > > my.ns.IdempotentLifecycle > > (start [this] > > (if (started? this) > > this > > (safe-start this))) > > (stop [this] > > (if (started? this) > > (safe-stop this) > > this))) > > > > So then a use case would like more like: > > > > (defrecord SillyExample [...] > > IdempotentLifecycle > > (started? [this] > > (custom-started-check this)) > > (safe-start [this] > > (custom-start-logic this)) > > (safe-stop [this] > > (custom-stop-logic this))) > > > > This seems like an easier end-user experience, but it feels wrong to > > implement a protocol with another protocol. A more "Clojurey" feeling > option > > would require changes to the component library itself: > > > > (defprotocol LifecycleStatus > > (started? [this])) > > > > (extend-protocol LifecycleStatus > > java.lang.Object > > (started? [_] false)) > > > > ;; Lifecycle protocol stays as-is > > > > (defn safe-start [component] > > (if (started? component) > > this > > (start component))) > > > > (defn safe-stop [component] > > (if (started? component) > > (stop component) > > this)) > > > > Then component would need to use safe-start/safe-stop in place of regular > > start/stop in the start-system/stop-system functions. > > > > Maybe this is better suited to an issue/pr on his repository, but I > wanted > > to see if there were any comments from the community. Is there a better > way > > to do this? > > > > Andrew Oberstar > > > > -- > > 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. > > -- > 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. > -- 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.