Thanks for the links to the talks. I enjoyed them. In particular, in "Thinking in Data", Stuart discussed a way to isolate side effects:
;; Bad (defn complex-process [state] (let [result (computation state)] (if (condition? result) (launch-missile) (erase-hard-drive)))) ;; Better (defn complex-process [state] (assoc state :analysis (computation state))) (defn decision [state] (assoc state :response (if (condition? (:analysis state)) :launch-missile :erase-hard-drive))) (defn defend-nation [state] (case (:response state) :launch-missle (launch-missile) :erase-hard-drive (erase-hard-drive))) The benefits of the second approach are that you don't have to use mocks to test the interesting parts of the code. However, I wonder how to expand this approach when the "state" itself requires IO - say, it exists in the DB. One approach would be to construct the full "state" before calling "complex-process" by querying the DB. But that would be inefficient especially if "complex-process" only conditionally needs some of the data. "That's more a matter of dependency injection, passing components around, and being careful to return a new object from each operation. Once you decouple your components from each other via some kind of abstract interface (protocols are nice for this), it would be relatively easy to create side-effect-free mock implementations for your tests or other use cases." Can you expand on that? It's very intriguing but I'm not quite seeing how it all fits together. One approach I've been trying is to have functions return a "request" (which describes some IO action) plus a callback. So, instead of reading directly from the DB, a function would describe a DB query that should occur, and then provide a callback for processing the result. When I write my tests, I can just test that the function returns the right request (or series of requests). Ben On Thursday, October 31, 2013 9:56:52 AM UTC-7, Gary Trakhman wrote: > > Well, though your DB is side-effects, your functions that write to it > don't have to be aware of that. That's more a matter of dependency > injection, passing components around, and being careful to return a new > object from each operation. > > Once you decouple your components from each other via some kind of > abstract interface (protocols are nice for this), it would be relatively > easy to create side-effect-free mock implementations for your tests or > other use cases. > > So, > 1. Create a suitable functional abstraction, where the side-effects are a > hidden implementation detail. > 2. Be rigorous about using it. > > We do this in our code via datomic, though we're not disciplined about > using a functional style with return-values from DB operations. Injecting > the database as a dependency is enough to help testing use-cases and > overall code decoupling, and the implementation is all hidden behind a set > of protocols specific to our app. > > > On Thu, Oct 31, 2013 at 6:31 AM, Jozef Wagner > <jozef....@gmail.com<javascript:> > > wrote: > >> Following presentations may help >> >> http://www.infoq.com/presentations/Clojure-Design-Patterns >> http://www.infoq.com/presentations/Thinking-in-Data >> >> JW >> >> >> On Thu, Oct 31, 2013 at 3:42 AM, Ben Brinckerhoff >> <bhbrinc...@gmail.com<javascript:> >> > wrote: >> >>> Clojure is the first functional programming language I've used for >>> anything more than toy examples, so I'm learning functional programming in >>> general as well as Clojure specifically. I understand the value of >>> creating pure functions in theory, but when writing applications, I'm >>> finding that logic and IO are getting hopelessly entangled. >>> >>> Specifically, in my web application, there is interaction with the DB on >>> most requests. The interaction may be quite complicated: e.g. first get >>> some user data, inspect it, and then make more DB calls if a user is >>> allowed to view some resource. >>> >>> Does anyone know of any books or articles on structuring functional code >>> to separate pure and impure functions? Or other resources? Projects that >>> are good examples? >>> >>> Although I've found good resources on writing pure functions and good >>> resources on using Clojure IO libraries, I haven't yet found anything that >>> talks about architectures that let you cleanly integrate the two in >>> real-world projects. >>> >>> Thanks, >>> Ben >>> >>> -- >>> -- >>> 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<javascript:> >>> 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 <javascript:> >>> 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 <javascript:>. >>> For more options, visit https://groups.google.com/groups/opt_out. >>> >> >> -- >> -- >> 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<javascript:> >> 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 <javascript:> >> 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 <javascript:>. >> For more options, visit https://groups.google.com/groups/opt_out. >> > > -- -- 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/groups/opt_out.