> > > > As it sounds a lot like your definition of OOP to me. > > For sure, this code relies on messages passing. It is "functional" in the > old sense that the messages are functions -- a usage of the word > "functional" that any Lisper would have understood for decades. But it is > not "functional" in the newer sense of "immutable". > > However, I avoid the things I dislike most about OOP: instantiating > objects, and having an outside Dependency Injection system that decides > what the dependencies of this code should be. As much as possible, I try to > write "start" and "stop" functions that take responsibility for initiating > whatever needs to be initiated for this namespace. > > > So it sounds like you went and re-built component, but this time > around message passing, and mutable actors.
And without records, which was the thing I felt was unnecessary in Component. A slightly simplified example: > > (ns mercury.message-queue > (:require > [mercury.datastore :as datastore] > [manifold.stream :as stream] > [manifold.deferred :as deferred] > [taoensso.timbre :as timbre] > [clojure.test :as test])) > > (def ^:private message-stream (atom nil)) > > (defn enqueue > [message eventual-result] > {:pre [ > (keyword? (:field-to-sort-by message)) > (number? (:offset message)) > (number? (:limit message)) > ]} > (stream/put! @message-stream > [ > (fn [db-connection] > (try > (datastore/execute message db-connection) > (catch Exception e (timbre/log :trace (str "The was an exception when we > tried to call the datastore layer: " e))))) > eventual-result > ])) > > (defn- worker > [db-connection] > (loop [message-vector @(stream/take! @message-stream)] > (let [message-fn (first message-vector) > eventual-result (second message-vector)] > (deliver eventual-result (message-fn db-connection))) > (if (= @message-stream ::stop) > (datastore/stop db-connection) > (recur @(stream/take! @message-stream))))) > > (defn start > [map-of-config-info] > (swap! message-stream (fn [old-stream] (stream/stream))) > (dotimes [_ 10] > (future (worker (datastore/start map-of-config-info))))) > > (defn stop [] > (swap! message-stream (fn [old-stream] ::stop))) > > > > The "start" method spins up some workers and makes sure they each have a > database connection. > The "stop" function sets in motion events that cause each worker to shut down its own database connection. > > > > On Sunday, June 21, 2015 at 7:10:42 PM UTC-4, tbc++ wrote: > I'd like to see an example of this "functional worker" style you mention. > As it sounds a lot like your definition of OOP to me. Not to mention that > anything that talks to a queue is performing io and is therefore not > functionally pure. So it sounds like you went and re-built component, but > this time around message passing, and mutable actors. > > On Sunday, June 21, 2015, Lawrence Krubner <lawr...@rollioforce.com> wrote: > There are ways to handle dependencies without going down the OOP route > that Stuart Sierra took. However, there are a lot of good ideas in Stuart > Sierra's Component library, and to the extent that you can borrow those > ideas, you end up with code that resembles "best practice" in the Clojure > community. > > For me, one "Big Idea" that I got from Stuart Sierra is that there should > be a "start" method that can be called either from the REPL or from -main > (for when you app runs as a daemon). The other "Big Idea" I got was that > there should be functions for handling the lifecycle of all the components > in your app, so you can easily start and stop and restart. So nowadays my > "core.clj" tends to look like this: > > (ns salesslick.core > (:gen-class) > (:require > [salesslick.start :as start] > [salesslick.stop :as stop])) > > ;; what you would call from the REPL to re-initate the app > (defn start [] > (try > (start/start) > (catch Exception e (println e)))) > > (defn stop [] > (try > (stop/stop) > (catch Exception e (println e)))) > > ;; Enable command-line invocation > (defn -main [& args] > (.addShutdownHook (Runtime/getRuntime) > (Thread. > #(do (println "Salesslick is shutting down") > (stop)))) > (start)) > > So I can call "start" and "stop" from the REPL, or, if the app is running > as a daemon, "start" gets called by -main, and "stop" is registered as a > shutDownHook. > > These are great ideas that I got from Stuart Sierra, however, I did not > feel the need to go as far toward OOP as Stuart Sierra did. And some of > things he has suggested as "best practice" really strike me as odd. To be > honest, some of the things he said were astonishing and went against > everything I have learned over the years. I'm thinking of what he says in > these 2 videos: > > http://www.infoq.com/presentations/Clojure-Large-scale-patterns-techniques > > Stuart Sierra - Components Just Enough Structure > > At one point he says that "Object Oriented code has the advantage that it > is obvious where you configure your code." > > Wow!!! What can I say about that!!! > > I have lost entire days because I was dragged into stupid, tedious > meetings whose subject was "How should we refactor these fat Rails models?" > > I've been dragged into incredibly boring meetings to discuss Rails versus > Sinatra, where the main thing under discussion was really the issue of > configuration. > > When I am on my deathbed, looking back, I will recall some days fondly, > and other days I will recall as wasted, and the most sadly wasted days of > all are the days I was forced to discuss Dependency Injection with my > co-workers. > > None of my experiences allow me to agree with Stuart Sierra that OOP makes > it obvious how to configure an app. > > Still, without a doubt, there are good ideas in Stuart Sierra's Component > library, and it is worth digging into them to find the good ideas. > > The place where I diverge from Stuart Sierra is in his use of Records. His > main concern seems to be making dependencies visible. It's the same issue > that Alex Miller focuses on here: > > http://tech.puredanger.com/2014/01/03/clojure-dependency-injection/ > > My own preference for making dependancies obvious is to use :pre > assertions. I'm looking at Typed Clojure as a possibility for going further > down that road. > > There might be use cases where it is fundamentally imperative to use > something like Stuart Sierra's pseudo-OOP style, but I have not met those > use cases yet. Most of the work I do tends to be the kind of thing where I > can spin up some internal "workers" and have them pull work off a queue. I > can initiate the workers from my "start" function and stop them with the > "stop" function. Typically, each worker will have its own connection to 1 > or more databases, and each worker takes responsibility for closing its > database connections once the "stop" function has been called. > > All of this is easy to do within the Functional Paradigm. > > I think there is a fascinating sociological question that haunts the > Clojure community, regarding OOP. Many of the best Clojure developers spent > 10 or 20 years doing OOP before they came to Clojure, so how much do they > bring OOP with them because they feel its natural, and they feel it natural > because they spent so many years with the OOP style? > > > > > > > > > > > > > > On Wednesday, June 17, 2015 at 10:15:21 PM UTC-4, Xiangtao Zhou wrote: > hi guys, > > Constructing simple clojure project is trival, just make functions. if the > project grows large, with more datasources, message queue, and other > storages, dependencies problem is on the table. > > One solution is stuartsierra/component, using system to configure > dependencies graph, make component and dependencies resolution separate. > > If we make namespace must run with code block that init the namespace, > like the "start" method in component, is this a good way to solve the > dependencies? > > because when the namespace is required the first time, the init block > worked once. > > any suggestion is appreciated. > > > - Joe > -- > 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. > > > -- > “One of the main causes of the fall of the Roman Empire was that–lacking > zero–they had no way to indicate successful termination of their C > programs.” > (Robert Firth) > -- 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.