views:

206

answers:

5
1: user=> (def some-account {:number :any­-number :balance :any­-balance :bank :any­-bank})
2: #'user/some-account
3: user=> (contains? some-account :bank)
4: true
5: user=> (assoc some-account :owner :any-owner)
6: {:owner :any-owner, :number :any­-number, :balance :any­-balance, :bank :any­-bank}
7: user=> (contains? some-account :owner)
8: false
9: user=> (def some-account (assoc some-account :owner :any-owner))
10: #'user/some-account
11: user=> (contains? some-account :owner)
12: true
13: user=> (dissoc some-account :owner)
14: {:number :any-­number, :balance :any­-balance, :bank :any-­bank}
15: user=> (contains? some-account :owner)
16: true

Can anyone explain this code?

Why, after (assoc some-account :owner :any-owner), does (contains? some-account :owner) return false?

Why, only after (def some-account (assoc some-account :owner :any-owner)) does (contains? some-account :owner) return true?

Why, after (dissoc some-account :owner), does (contains? some-account :owner) returns true?

I tried saying (def some-account (assoc some-account :owner :any-owner)) out of instinct. But why does it work this way?

+6  A: 

Maps in Clojure are immutable.

That is, the updation functions instead of modifying the original map, return a new updated map.

Try the following:

user=> (def some-account {:number :any­-number :balance :any­-balance :bank :any-bank})
#'user/some-account
user=> (contains? some-account :bank)
true
user=> (def updated-map (assoc some-account :owner :any-owner))
#'user/updated-map
user=> (contains? updated-map :owner)
true
user=>
missingfaktor
nice introduction into clean coding :)
Belun
+2  A: 

In Clojure all the data types are immutable. Hence the assoc operation on the some-account map does not change it in place (unlike the put operation on java.util.Map in Java) and produces a new map. That is why the countain? operation returns false. When you do

(def some-account (assoc some-account :owner :any-owner))

you are catching the return value of the assoc operation and assigning it to the some-account variable. In a sense you are redefining it. So later the contain? operation returns true.

abhin4v
+1  A: 

because assoc and dissoc return new objekts and don't change some-account

+3  A: 

It's always good to note that when the update functions produce a new map, they do not copy the old one they produce a new one that shares all the parts that did not change with the old one and only replaces the parts required by the changes.

This structural sharing is very important to all functional languages with [immutable data structures][1]

[1]: http://clojure.org/functional_programming#Functional Programming--Immutable Data Structures

Arthur Ulfeldt
+1  A: 

Imagine that you were using a number instead of a map:

user> (def s 3)
#'user/s
user> (= s 3)
true
user> (+ 1 s)
4
user> (= s 4)
false
user> (def s (+ 1 s)) ;;don't do this! changing definitions shouldn't be used in a program, only at the REPL for trying things out!
#'user/s
user> (= s 4)
true
user> (- s 1)
3
user> (= s 4)

In Clojure, most values behave like numbers. There are some mutable things, but they are hidden behind cryptic interfaces. An awful lot of programming can be done without them.

But how can we program without mutating variables? You may have seen the factorial function

(defn factorial [n]
        (if (< n 2) 1
            (* n (factorial (dec n)))))

user> (factorial 5)
120

Here is a similar function that builds up a map in the same way

(defn char-map [n]
        (if (< n 0) {}
            (assoc (char-map (dec n)) n (char n))))

user> (char-map 10)
{various control characters..}

This style is strange at first, but eventually becomes natural. When I think of something to program these days, I often think of the recursive solution before the imperative loop.

They are two different ways of looking at the same thing.

It is usually easy to translate between them, so that if you can think of one way to do it, you have already thought of the other. But this does take practice, like learning to speak Latin.

There are some advantages in clarity of thought and thread safety to having things not change after you've created them. Mathematicians particularly seem to prefer it.

John Lawrence Aspden