Значения оцениваются сами по себе, а блок do возвращает результат последнего выражения. Вот почему (do 2) возвращает 2, в примере ниже. Поскольку он возвращает 2, при получении значения из REPL 2 будет выведено на экран в результате.
Однако функция println вызывает побочный эффект, но возвращает ноль. Побочным эффектом ниже является то, что значение 2 будет напечатано на стандартный вывод. Также печатается nil, потому что это возвращаемое значение из функции println.
user=> (do 2)
2
user=> (println 2)
2
nil
В вашем примере выходные данные не одинаковы из-за того, что я объяснил выше. Заметьте разницу сами:
user=> (def x (for [i (range 1 3)] (do (println i) i)))
#'user/x
user=> x
(1
2
1 2)
user=> (def x (for [i (range 1 3)] (do i)))
#'user/x
user=> x
(1 2)
Также обратите внимание, что для ленивый :
(def x (for [i (range 1 3)] (do (println i) i)))
#'user/x
user=> x
(1
2
1 2)
user=> x
(1 2)
Во второй раз, когда запрашивается x, печатается только (1 2). Зачем? Потому что ленится. Он создает свои элементы только в первый раз, когда они запрашиваются (в первый раз, когда запрашивается x). В следующий раз элементы уже произведены, поэтому побочный эффект внутри do больше не повторится.