Migrating from Java/Spring to Clojure

526 views
Skip to first unread message

John Krasnay

unread,
Jan 31, 2016, 4:19:17 PM1/31/16
to Clojure
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?

Thanks.

jk

Sean Corfield

unread,
Jan 31, 2016, 9:11:23 PM1/31/16
to Clojure Mailing List
John Krasnay wrote on Sunday, January 31, 2016 at 10:11 AM:
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.

See https://github.com/seancorfield/engine which I built to investigate exactly that approach. We haven’t started using it at work (World Singles) but that is the plan so I’d be interested in getting more feedback on it. I have not yet released it to Clojars.

Sean


Mikera

unread,
Feb 1, 2016, 1:28:41 AM2/1/16
to Clojure
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. 
 

Colin Yates

unread,
Feb 1, 2016, 4:15:20 AM2/1/16
to clo...@googlegroups.com
+1. I also challenge the starting condition that most code will be data
access - that was my assumption (when I migrated from Spring/Hibernate).
It doesn't take much rigour to ensure every side effecting fn
is _only_ concerned with the side effect leaving lots of
non-side-effecting code.

Also, do remember that clojure.jdbc works on plain Clojure maps so you
get half-way there simply by returning the rows to persist.

My advice would be to do the 'Clojure' thing and isolate every
side-effecting code (idiomatically post-fixed with the '!') and see how
it goes.

Although Mikera might have unintentionally put you off it, I would
highly recommend Prismatic Schema from day one :-).

John Krasnay

unread,
Feb 1, 2016, 12:55:08 PM2/1/16
to Clojure
Thanks, Sean. That's exactly the sort of thing I was looking for.

jk

John Krasnay

unread,
Feb 1, 2016, 1:23:04 PM2/1/16
to Clojure
Thanks for the feedback. I wasn't sure whether mocks were frowned upon but it seems like a pretty standard approach.

BTW I really like how the Clojure community has this pragmatic feel about it. Aim for pure functions, but don't bend over backwards where it doesn't naturally fit.

jk
Reply all
Reply to author
Forward
0 new messages