beginners

2026-04-23T10:22:25.099659Z

I have been reading about binding but did not really understand what makes it different from let. Do you have any example of use case where using binding is more correct than a let call?

hrtmt brng 2026-04-25T15:18:35.712799Z

This concept I think does not exist in many other languages. So it is a bit unexpected when learning Clojure, that such variables are possible.

hrtmt brng 2026-04-25T15:20:40.313839Z

Global variables that can be changed locally. And if you leave the local scope, they go back to the original value.

2026-04-25T15:53:17.717729Z

In fact, on top of my mind, I cannot remember any other OO language that I used before that contains such type of concept. Seems to be something interesting, but I still cannot imagine real use cases where this is the "correct" approach for a problem. Maybe, some type of configuration overriding, where some values of configuration are based on vars? Btw, I am checking "Clojure for the Brave and True" because I remember of an introduction for bindings, and later I'll take a look at "Clojure High Performance Programming".

p-himik 2026-04-25T15:59:47.432349Z

> I still cannot imagine real use cases where this is the "correct" approach for a problem. print, for example, and all its friends. How else would you replicate all the print functionality, given that some functions that your code uses might use print themselves with specific overrides that they care about. It's not just configuration override, it's local override - applied to a particular scope, having no effect outside of that scope.

phronmophobic 2026-04-25T17:12:29.807389Z

It’s essentially dynamic scoping. https://en.wikipedia.org/wiki/Scope_(computer_programming)#Dynamic_scope

p-himik 2026-04-23T10:33:21.927459Z

let is about local bindings. When you do (let [a 10] ...), a is a local constant with value 10, nothing else. binding is about vars - things that exist at the level of a namespace, not locally. It basically temporarily puts a value into a globally available location.

olli 2026-04-23T10:52:13.856119Z

(def ^:dynamic language :en)

(defn say-hello []
  (get {:en "hi!"
        :sv "hej!"}
       language))
(say-hello)
;=> "hi!"

(defn in-swedish [do-stuff]
  (binding [language :sv]
    (do-stuff)))

(in-swedish say-hello)
;=> "hej!"

(let [language :sv]
  (say-hello))
;=> "hi!"

p-himik 2026-04-23T10:59:27.280599Z

Ah, and the answer the explicit question explicitly - you use binding when you use dynamic vars that you have to temporarily override. And you use such vars when you have no better mechanism of temporarily changing how something works by default. Most often, it's possible and even better to do without them - by passing explicit config maps around. But sometimes it's either impossible or incredibly cumbersome. Like all the *print-...* vars - imagine that you'd have to add a map to each and every call of print and all the related functions. It's just not possible when you want to alter how some existing code prints stuff without altering that code.

2026-04-23T12:53:42.201939Z

So, can I consider vars something like "global variables"?

p-himik 2026-04-23T13:46:07.863889Z

Kinda, but more flexible since you can dynamically create or remove them and also temporarily override them in a thread-safe way. But I'd suggest viewing them as "global constants that, if absolutely necessary, could be changed". Otherwise you'd be in the world of stateful place-oriented programming world of pain.

phronmophobic 2026-04-23T17:49:55.370749Z

> in a thread-safe way There is a huge caveat to this statement which is that vars use thread locals. They are not the best tool for sharing mutable data across multiple threads.

👍 1