Sure, that makes sense. I'll give that a go along with a few other ideas,
and see what works out best. Thanks for the help!

Andrew Oberstar

On Sun, Mar 15, 2015 at 1:22 PM Colin Yates <colin.ya...@gmail.com> wrote:

> I don't have one at hand (as I literally wrote my first macro last
> week ;)) but the way it could work is something like:
>
> (defmacro idempotent-component
>   [{:keys [name start started? stop]}]
>   `(defrecord (symbol name) [...]
>   component/Lifecycle
>   (start [this#]
>     (if (~'started? this#)
>       this#
>       (~' this)))
>   (stop [this#]
>     (if (~'started? this#)
>       (~`stop this#)
>       this)))
>
> and called like
>
> (idempotent-component {:name "SillyExample" start: println started?:
> (constantly true) stop: println})
>
> There are probably a 100 things wrong with that macro and possiblly
> even the idea of using a macro here, but hopefully it opens up the
> possibilities.
>
> On 15 March 2015 at 17:40, Andrew Oberstar <ajobers...@gmail.com> wrote:
> > 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.
>
> --
> 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.

Reply via email to