On Jul 8, 8:40 pm, Brian Carper <
briancar...@gmail.com> wrote:
> So pretty much every function needs to have the session as a
> parameter, and most of them just pass it along to other functions
> without doing anything with it. I have similar needs for the params
> and headers and flash. Passing all of this around to every function is
> painful.
>
> How do you deal with this? I come from a Rails background where
> params and session are always easily accessible. I like this for
> convenience even though it isn't as "functional".
This answer might be a little lengthy, so please bear with me.
In computer software, bugs are caused when a program enters an
unforeseen state. So to prevent bugs is equivalent to reducing
unpredictability.
Functional programming is a way of reducing unpredictability by
eliminating implicit state. In a pure functional language like
Haskell, functions are free of side-effects and this makes them more
predictable. For instance, a function in Haskell will always return
the same result, given the same arguments.
In a more general sense, what functional programming languages like
Haskell do is limit the flow of information: only data explicitly
passed through a functions arguments affect its results. We can
additionally constrain this data by adding strict type checking, and
Haskell provides developers with a very sophisticated type system.
Since Haskell is a very good language for reducing bugs, why do I use
Clojure?
Basically because robustness is not the only measure of a programming
language. Clojure sacrifices static typing and allows some side
effects in return for greater flexibility and conciseness. In my view,
Clojure strikes a better balance between flexibility and robustness.
Global bindings require a similar trade-off. You're trading robustness
for conciseness, but the trick is to ensure that you're getting the
right balance.
Let's now consider your application. Your views depend strongly on
whether the user is logged in or not. You probably also need
information like their username and some for of user ID. Assuming
that's all you need:
(declare current-user)
(defn with-user-binding [handler]
(fn [request]
(binding [current-user (-> request :session :user)]
(handler request))))
(defroutes user-routes
...)
(decorate user-routes with-user-binding)
In the above case, I'm using exactly one binding, and I'm limiting it
to user-routes, which is a collection of all the routes that I know
will need the current-user binding. In the above example, I'm passing
no more information than is required to my functions.
In Compojure, you should think of routes as a gateway; something that
not only defines how browsers can access your application, but also
something that determines what information is allowed to pass through
to your internal logic.
In my opinion, a philosophy of information restriction like this
results in programs that have less bugs, are more secure, and are
easier to refactor.
- James