3

I'm just getting started with Clojure and I have no fp experience but the first thing that I've noticed is a heavy emphasis on immutability. I'm a bit confused by the emphasis, however. It looks like you can re-def global variables easily, essentially giving you a way to change state. The most significant difference that I can see is that function arguments are passed by value and can't be re-def(ined) within the function. Here's a repl snippet that shows what I mean:

 towers.core=> (def a "The initial string")
 #'towers.core/a
 towers.core=> a
 "The initial string"
 towers.core=> (defn mod_a [aStr]
     #_=>   (prn aStr)
     #_=>   (prn a)
     #_=>   (def aStr "A different string")
     #_=>   (def a "A More Different string")
     #_=>   (prn aStr)
     #_=>   (prn a))
 #'towers.core/mod_a
 towers.core=> a
 "The initial string"
 towers.core=> (mod_a a)
 "The initial string"
 "The initial string"
 "The initial string"
 "A More Different string"
 nil
 towers.core=> a
 "A More Different string"

If I begin my understanding of immutability in clojure by thinking of it as pass-by-value, what am I missing?

peterc
  • 97
  • 8
  • Java Arrays are passed by value, but if you change their contents in one function another function will be affected by the change. Clojure vectors don't do that. – noisesmith Dec 10 '13 at 18:29
  • @noisesmith Java pass-by-value is odd, though. The value being passed is a pointer to an object, which allows you to change the object. But, I don't think, you can change the pointer. – peterc Dec 10 '13 at 20:43
  • Java pass-by-value is Clojure pass-by-value. The importance is whether the value passed has mutable internal state. – noisesmith Dec 10 '13 at 21:09

5 Answers5

9

Call-by-value and immutability are two entirely distinct concepts. Indeed, one of the advantages of variable immutability is that such variables could be passed by name or reference without any effect on programme behaviour.

In short: don't think of them as linked.

Marcin
  • 48,559
  • 18
  • 128
  • 201
  • Fair enough. When I read that data were "immutable" I thought that would mean that global variables were all "final" (in Java speak). But it doesn't look that way. I realize that under the covers I'm not changing the _value_ but that just seems like semantics. The next function to use the global (_a_ in my example) will get the modified string. – peterc Dec 10 '13 at 20:56
4

generally very little is "def"d in a clojure script/class, it's mostly used for generating values that are used outside of the class. instead values are created in let bindings as you need them in your methods.

def is used to define vars, as stated in Clojure Programming:

top level functions and values are all stored in vars, which are defined within the current namespace using the def special form or one of its derivatives.

Your use of def inside a function isn't making a local variable, it's creating a new global var, and you're effectively replacing the old reference with a new one each time.

When you move onto using let, you'll see how immutability works, for instance using things like seqs which can be used over without penalty of something else having also read them (like an iteration over a list would in java for instance), e.g.

(let [myseq (seq [1 2 3 4 5])
          f (first myseq)
          s (second myseq) 
        sum (reduce + myseq)]
  (println f s sum))
;; 1 2 15

As you can see, it doesn't matter that (first myseq) has "taken" an item from the sequence. because the sequence myseq is immutable, it's still the same, and unaffected by the operations on it. Also, notice that there isn't a single def in the code above, the assignment happened in the let bindings where the values myseq, f, s and sum were created (and are immutable within the rest of the sexp).

Mark Fisher
  • 9,838
  • 3
  • 32
  • 38
  • Yes, I've done some trivial examples with let and I think I see how Clojure gives you some solid tools to avoid stepping on your data. I'm just a bit surprised that I'm able to clobber global variables. – peterc Dec 10 '13 at 20:58
  • 1
    One difference in design policy between clojure and most languages is that in clojure mutable global state is often considered preferable to mutable local state. The tradition of lisp is to develop entirely in an interactive environment, which makes the ability to redefine your top level bindings necessary. – noisesmith Dec 10 '13 at 21:30
  • "able to clobber global variables" - i completely understand, when i first started learning clojure i had the same thought, "hang on you told me it's all immutable but i can redefine this so easy!", hence why i offered my own "ah-i-get-it" moment when i realised that def is frankly only used to expose the outer layers of something useful, and also in repls when trying stuff out "(def does-this-work (some-function-ive-written blah blah)". – Mark Fisher Dec 11 '13 at 00:38
3

Yes, immutability is different from pass-by-value, and you've missed a couple of important details of what's going on in your examples:

  1. value mutation versus variable re-binding. Your code exemplifies re-binding, but doesn't actually mutate values.

  2. shadowing. Your local aStr shadows your global aStr, so you can't see the global one -- although it's still there -- so there's no difference between the effects of (def a ...) and (def aStr ...) here. You can verify that the global is created after running your function.

A final point: Clojure doesn't force you to be purely functional -- it has escape hatches, and it's up to you to use them responsibly. Rebinding variables is one of those escape hatches.

Matt Fenwick
  • 48,199
  • 22
  • 128
  • 192
2

just a note that technically Java, and by extension Clojure (on the JVM) is strictly pass by value. In many cases the thing passed is a reference to a structure that others may be reading, though because it is immutable nobody will be changing out from under you. The important point being that mutability and immutability happen after you pass the reference to something so, and Marcin points out they really are distinct.

Community
  • 1
  • 1
Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284
0

I think of much of the immutability in Clojure as residing in (most of) the built-in data structure types and (most of) the functions that allow manipulating ... uh, no, modifying ... no, really, constructing new data structures from them. There are array-like things, but you can't modify them, there are lists, but you can't modify them, there are hash maps, but you can't modify them, etc., and the standard tools for using them actually create new data structures even when they look, to a novice, as if they're performing in-place modifications. And all of that does add up to a big difference.

Mars
  • 8,689
  • 2
  • 42
  • 70