Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understand Clojure binding syntax

I am learning Clojure and was reading about doseq when I noticed an example like below on the official Clojure doc for doseq

(doseq [x [-1 0 1]
        y [1  2 3]] 
  (prn (* x y)))

My confusion is with the expression [x [-1 0 1] y [1 2 3]].

Does this signify a binding expression? I tried some amount of google search but could not come across any documentation that describes such a form.

Could someone help me with understanding the various syntax representations for binding forms in Clojure?

like image 278
Prahalad Deshpande Avatar asked Jun 29 '17 17:06

Prahalad Deshpande


2 Answers

This is a "binding form" in that it "binds" the values from the expression to the name x in turn. So it colloquially means an expression that binds names to values. This is part of "destructuring binding forms" which bind names to parts of a compound value such as a list or map.

The term "binding" instead of "setting" helps convey the difference between what this is doing and setting variables in some other programming languages. The name is attached to the value for the time it takes for the form inside the doseq to run, then the name is released to be attached to another value.

Clojure offers arbitrary structural binding to give names to any part of a value in most places in the language that assign names (bind symbols)

(doseq [[x1 x2] [[-1 -1] [0 0] [1 1]]
        y [1  2 3]] 
  (prn (* x1 x2 y)))

is also a binding expression though it looks slightly deeper into the data and assigns names to two values per item in the first vector (and assumes there are two numbers in each of them) I'm very fond of this tutorial on destructuring/binding

like image 168
Arthur Ulfeldt Avatar answered Oct 12 '22 12:10

Arthur Ulfeldt


It is like a nested for loop in Java. You can also do the nesting "manually":

(dotest
  (newline)
  (println "version 1")
  (doseq [x [1 2]
          y [:a :b :c]]
    (println  x y))

  (newline)
  (println "version 2")
  (doseq [x [1 2]]
    (doseq [y [:a :b :c]]
      (println x y))))

with results:
version 1
1 :a
1 :b
1 :c
2 :a
2 :b
2 :c

version 2
1 :a
1 :b
1 :c
2 :a
2 :b
2 :c

Note that doseq always returns nil, and is only good for generating side-effects (like printing to the screen).

A for expression behaves similarly, but returns a (lazy) sequence of values (note we are generating a vector [x y] on each loop):

(newline)
(println "For generates a (lazy) sequence:"
  (for [x [1 2]
        y [:a :b :c]]
    [x y]))

with result:

For generates a (lazy) sequence: ([1 :a] [1 :b] [1 :c] [2 :a] [2 :b] [2 :c])
like image 2
Alan Thompson Avatar answered Oct 12 '22 14:10

Alan Thompson