The meaning of def

21 views
Skip to first unread message

Robert

unread,
Jun 5, 2012, 5:52:02 AM6/5/12
to london-c...@googlegroups.com
The meaning of def came up in the last dojo. def creates an identity and assigns it a value. However that value can be changed while be consistently read. So def isn't the same as static final which people might find confusing, re-def'ing does NOT cause an error.

Not only that but you can use def within a function to redefine the value of an identity in any namespace the function has access to.

So I found out this at a dojo where a group had cunning used this feature and the question came up then whether it is a good idea and how this compares with atoms.

Well as far as I can tell re'def-ing is the same as using atoms and reset! so it is perhaps a matter of taste. However set! does allow you to define a function on the current value so it is much more powerful than re'def-ing.

Using atoms is more explicit in identifying your state and I've found them a more effective way of introducing interactive change in things like Processing applets.

Clojure experts are now more than welcome to chime in with their opinions!

Let's look at a REPL output that illustrates this:

user=> (def p 3)
#'user/p
user=> p
3
user=> (def p 7)
#'user/p
user=> p
7
user=> (defn jeff [] (def p (inc p)))
#'user/jeff
user=> (jeff)
#'user/p
user=> p
8


Philip Potter

unread,
Jun 5, 2012, 6:06:50 AM6/5/12
to london-c...@googlegroups.com

I largely agree with your summary (except did you mean swap! instead of set! ?). I also freely admit I was in the guilty group who used re-def to manage state; however the reason we did this was because we didn't know any better. I'd use atoms for the same task now.

I'd add:

* atoms are updated atomically and don't suffer the lost update problem, provided you use swap! and not reset! Avoiding lost updates is a core part of all of clojure's concurrent state variables: atoms, refs and agents all provide means to avoid lost updates, largely through using functions to transform current state into new state. Vars do not share this property.
* vars (the stateful variables created by def) provide access to thread-local storage by using dynamic rebinding (the #'binding macro).
* re-deffing of vars is something which I only really use for repl-based programming. The fact that I can rebind a var means that I can interactively replace a broken function or refactor a messy one. But I wouldn't want to programmatically re-def anything.

Phil

--
You received this message because you are subscribed to the Google Groups "London Clojurians" group.
To view this discussion on the web visit https://groups.google.com/d/msg/london-clojurians/-/jMzM0p555qAJ.
To post to this group, send email to london-c...@googlegroups.com.
To unsubscribe from this group, send email to london-clojuri...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/london-clojurians?hl=en.

James Aley

unread,
Jun 5, 2012, 6:39:39 AM6/5/12
to london-c...@googlegroups.com
I was just having a conversation with a colleague about def. This feels relevant, although I apologise if it turns out to be the thin end of the wedge that initiated a massive tangent :-)

I was commenting on our use of def, and suggesting that we perhaps over-use it for holding atoms, etc. There are a few areas in our (mostly 'my') code where I think holding the atom in a closure would've been a better option. So, instead of def, I mean doing something like this:

(defn make-counter
  "Return a counter function with the provided starting value."
  [start-at]
  (let [counter (atom start-at)]
    (fn [] (swap! counter inc))))

Obviously, in making the example trivial, I've made the classic mistake of taking away all relation to the real world, so use your imagination a bit. All I wanted to add to the topic was that it's far too easy to just hold state in a def even though that state might only be relevant to a single function, so having a naked def exposing your state to the rest of the namespace feels unnecessary. We have proper encapsulation in the form of closures, and I for one have been neglecting this!


Jaley.

Robert

unread,
Jun 5, 2012, 10:27:01 AM6/5/12
to london-c...@googlegroups.com
Interestingly this intersects with some of the things I took away from Stuart's Halloway's keynote at EuroClojure along with with my own misgivings about macros.

I think you're right about us generally not really thinking about whether we want to expose a data point on a process in a namespace. In general we probably want to do the latter.

However the issue of the closure is that it reduces utility by hiding the components away from the consumer. I can't read that counter value without worrying about whether the side-effect is valid.

Unfortunately I can't think of a non-trivial example either at the moment, but I suspect that if you are writing code for consumption as a library you want the consumer to provide or manage the state to be manipulated and that if you are writing your own code then going to great lengths to hide your state is probably just tying your hands needlessly.

Philip Potter

unread,
Jun 5, 2012, 11:25:42 AM6/5/12
to london-c...@googlegroups.com
There is a middle-ground, which is (def ^:private ...) - you still
have a named var, but it's private to the namespace.

mccraigmccraig of the clan mccraig

unread,
Jun 5, 2012, 11:58:40 AM6/5/12
to london-c...@googlegroups.com
using a closure as your api gives you a lot more flexibility than directly exposing a var containing an atom :

as a program grows and requires more of it's components, i find it is a common refactoring to replace a var directly referencing a value with a factory function which creates a closure which returns the value

:c

Julian Birch

unread,
Jun 5, 2012, 1:19:09 PM6/5/12
to london-c...@googlegroups.com
I think there are a couple of intended uses for def: rebinding in tests is very useful and allows you to fake out plenty of things that don't look fakeable.  Dynamic binding can also be used for multi-tenancy scenarios (as well as stuff like logging).

Straight forward redefining could also be used by code that reads in configuration.

Anyone else got any practical uses for defs?  (i.e. not entries for the obfuscated clojure competition)
--
Sent from an iPhone, please excuse brevity and typos.

Jeff Rose

unread,
Jun 5, 2012, 1:51:25 PM6/5/12
to london-c...@googlegroups.com
Generally I think def should be used to define constants in your program that are not expected to change. That said, during development we are constantly redefining both constant values and functions (defn is just short for (def (fn [] …)) anyways), so there is no problem with calling def multiple times while working on some code or experimenting, in fact this is what repl driven development is all about.

Regarding def, although it does work, I think generally it is considered poor form to use it inside of a function, as this is basically returning to imperative programming with mutable variables. (Interesting how something as mundane as a variable in most languages can become so taboo…) Anywhere you would be using def inside of a function it should probably be using either an atom or a dynamic variable with *earmuffs*. Dynamic bindings create other headaches though, so they should be used sparingly if ever.

-Jeff
> > To post to this group, send email to london-c...@googlegroups.com (javascript:_e({}, 'cvml', 'london-c...@googlegroups.com');).
> > To unsubscribe from this group, send email to london-clojuri...@googlegroups.com (javascript:_e({}, 'cvml', 'london-clojurians%2Bunsu...@googlegroups.com');).
> > For more options, visit this group at http://groups.google.com/group/london-clojurians?hl=en.
>
>
>
> --
> Sent from an iPhone, please excuse brevity and typos.
> --
> You received this message because you are subscribed to the Google Groups "London Clojurians" group.
> To post to this group, send email to london-c...@googlegroups.com (mailto:london-c...@googlegroups.com).
> To unsubscribe from this group, send email to london-clojuri...@googlegroups.com (mailto:london-clojuri...@googlegroups.com).
Reply all
Reply to author
Forward
0 new messages