0

I've got a bit of a functional question.

If I have a functional chain where I compose functions in a way that the output of the previous is the input of the next one.

And somewhere down that chain, I need stuff to do IO, like a client to a DB, etc.

What would be a Clojure way that feels functional to do it?

=========

To explain it more clearly,

(let [map {:aThing "I'm a"}]
(->> (a->b map)
     (b->c)
     (c->d)
     (d->e)))
  1. (a->b) converts :aThing into :bThing
  2. (b->c) converts :bThing into :cThing but it needs something that can AES decrypt to do so.
  3. (c->d) converts :cThing into :dThing but it does so by fetching things from a database, and thus requires a database username/pasword, endpoint, connection, etc.
  4. (d->e) converts :dThing into :eThing and returns it.
  5. The chain is complete.

In Clojure, what would be proper way to allow this chain to work?

Didier A.
  • 4,609
  • 2
  • 43
  • 45
  • To me, I wouldn't bother writing pure functional codes. When it becomes a burden, get rid of it. – Davyzhu Nov 13 '15 at 04:12
  • @Davyzhu I'm not looking for a pure answer, I'm unpure just by the fact I need to do IO. I'm more looking for elegant solutions that are somewhat true to the functional style. So unpure functional answers are welcomed too, as long as they lead to code that's readable and maintainable. – Didier A. Nov 14 '15 at 23:33

1 Answers1

0

If a tree falls in the woods, does it make a sound? If a pure function mutates some local data in order to produce an immutable return value, is that ok? -- Rich Hickey

I say Davyzhu is right.

The way I'd approach it is this:

(def conn-opts {:user :name
                :password :pass
                ;any other params})

(defn c->d [conn-opts map]
 (let [db (connect conn-opts)]
      ;return the result of applying the transformation 
      ;on the map and the db.
     (fn-do map db))

(defn job [map]
 (->> map
      (a->b)
      (b->c)
      (c->d conn-opts)
      (d->e)))
Mark Fisher
  • 9,838
  • 3
  • 32
  • 38
Ricardo Acuna
  • 530
  • 2
  • 10
  • I like it. Hadn't heard that quote from Rich Hickey before, very cool. I see this as the Dependency Injection way. How would you say this is better/worst then the Pass-through way, where I would just add to map the conn-opts and have all function carry the key/values down to (c->d)? – Didier A. Nov 14 '15 at 23:48
  • It depends on your domain model of course. As a matter of separation of concerns I wouldn't usually merge the data with the system configuration. The same reason we don't use object-oriented design in functional programming, we don't want methods obscuring our object's properties. You might have some luck with Stuart Sierra's [component framework](https://github.com/stuartsierra/component) it showcases a way to manage the dependencies of a system. – Ricardo Acuna Nov 15 '15 at 22:44