On Monday, 1 February 2016 05:19:17 UTC+8, John Krasnay wrote: > > Hi all, > > I'm migrating an application from Java/Spring to Clojure and I'm searching > for a good, functional approach. The app exposes a REST interface using > compojure-api and primarily interacts with a relational database and sends > email. > > I think most people end up passing their database connection (and other > stateful resources as required) around to non-pure functions that do the > side-effectful work, while trying to push as much business logic into pure > functions possible. My problem with this approach is that most of my app's > functionality is in database interactions, so most of my functionality ends > up being non-pure and difficult to test. For example, to unit test my > functions it seems I'd have to mock my database connection. > > Instead of this, I'm considering an approach where my functions instead > return a data structure containing a description of the side-effects to be > performed (e.g. "insert these rows into this table", "send this email", > ...), and having a single non-pure function that does all the > side-effectful work described by the structure. > > From what I've read, it seems is sort of how Haskell does IO in a pure > functional manner, using their IO monad. > > Has anyone tried this sort of approach? Are there any libraries that might > help? Any pitfalls I might be setting myself up for? >
I'm sure that you could get this approach to work, but it sets off some alarm bells in my head. Problems you may face: a) You are effectively developing a programming language, where your state-affecting code is represented in data. Clojure already does that.... why reinvent the wheel? I'm reminded of Grrenspun's tenth rule of programming: "Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp." b) Representing operations in code seems neat, but it gets tricky when you have dependencies between them. What if one operation requires conditional execution that depends on the result of a previous operation? You'll need branching and control flow to handle this in a general way, which is non-trivial. What if one set of side effects writes to places that are subsequently "read" by later operations? You'll need to mock or model a database engine if you want to test / simulate this directly. c) Clojure is an dynamically typed language (sadly, one of it's few flaws....) . The lack of compiler support for type verification makes it harder to keep track of exactly the type of data passing through your deeply composed functions and data structures. Trying to represent graphs of operations as data as well is only likely to make things worse. Yes there are tools such as Schema, but they have their own cost in terms of runtime performance (if you use them for validation) and runtime complexity. It's possible to get right, but I would predict quite a lot of pain along the way. I'd strongly suggest trying this the more conventional Clojure way first (e.g. using Stuart Sierra's component approach). This can get pretty close to being a nice functional style, and is very easy to test (you just need to mock one or two components and you are usually good). You may ultimately find some areas why you want to implement a DSL, but trying to write the whole application in this fashion from the beginning seems to me like a bit of a high risk strategy. -- 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.