user1172468
user1172468

Reputation: 5464

Clojure: understaning the binding in a doseq

I understand the following snippet of code and its corresponding output

(let  [ [x y] (map list [1 2] [3 4])]  (prn x) (prn y))
(1 3)
(2 4)
nil

Now the following output confuses me:

(doseq [ [x y] (map list [1 2] [3 4])] (prn x y))
1 3
2 4
nil

I think in the above snippet x will get bound to [1 3] and y will get bound to [2 4] so the output should be"

1 2
3 4
nil

Upvotes: 2

Views: 383

Answers (2)

Thumbnail
Thumbnail

Reputation: 13473

Let's isolate the source of your confusion.

(map list [1 2] [3 4])

evaluates to

((1 3) (2 4))

So your first example

(let [[x y] (map list [1 2] [3 4])]  (prn x) (prn y))

... is equivalent to

(let [[x y] [[1 3] [2 4]]] (prn x) (prn y))

... or, printing slightly differently

(let [[x y] [[1 3] [2 4]]] (prn [x y]))

... which simplifies to

(let [z [[1 3] [2 4]]] (prn z))

... producing, as expected,

; [[1 3] [2 4]]
; nil

So far, so good.

If we boil down the confusing example,

(doseq [ [x y] (map list [1 2] [3 4])] (prn x y))

... in the same way, we get, taking the liberty of printing each z whole,

(doseq [z [[1 3] [2 4]]] (prn z))

which fairly clearly produces the observed order:

[1 3]
[2 4]
nil

The difference is that the doseq binds z to each successive vector in [[1 3] [2 4]], so we don't see an enclosing [ ... ], whereas the let binds z once to the whole thing.

Upvotes: 3

johnbakers
johnbakers

Reputation: 24750

The binding is pulling out the individual elements inside the larger single element in the nesting. The map creates a list ((1 3) (2 4) so the element 1 3 is first and thus that is what the doseq outputs: this is "destructuring" and the x and y are both bound from inside a single element of the list. Thus x and y are 1 and 3, and then 2 and 4.

Note also that this is the same binding that occurs in a for and the destructuring works for all sequence types.

Upvotes: 3

Related Questions