On 31 July 2015 at 09:54, J. Pablo Fernández <pup...@pupeno.com> wrote:
>
> For me, the thing is, I have a traditional relational database here, this
> is already far from pure. For example, calling (db/create-user "
> pup...@pupeno.com") twice will not only not return the same thing the
> second time, it'll actually raise an exception the second time. Also, the
> database connection is *global state* unless each function creates its own
> connection, which would be terrible. So, this global state also breaks
> functional purity.
>
> The problem with the second aspect of breaking purity as far as I can see
> is this: at some point, this global state has to be picked up and used, so
> at some point a function will *not* get a database connection passed to it
> but *will* access the database by using this global connection.
>

Why does the database connection have to be global state? Why not pass it
through your functions as an argument?

So in your example scenario, you might write something like:

  (defn create-user [db user]
    (sql/insert! db "users" user))

  (defn create [db user]
    (create-user db (update user :password encrypt-password)))

  (defn register [db user]
    (create db user)
    (send-welcome-email user))

  (defn make-handler [{:keys [db]}]
    (routes
     (POST "/users" [& user]
       (register db (select-keys user [:email :password])))))

This style ensures that the functions only have access to the database you
want them to have access to, and also allows you to deal with multiple
databases simultaneously. For example, often I'll have a dev database and a
test database, and ideally I want to be able to run my tests in my REPL (or
via Cider) without clearing the data in my dev database.

Another advantage of this approach is in test performance. Frequently I'll
place my lowest database functions in a protocol:

  (defprotocol UserDatabase
    (create-user [db user]))

This allows me to implement a mock database for performing fast integration
tests.

While most databases are stateful, so we obviously sacrifice purity, that
doesn't mean we shouldn't continue limiting the use of other dangerous
tools like dynamic or global vars. Clojure is a pragmatic language, but
this means that it just trusts us to be disciplined.

- James

-- 
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.

Reply via email to