Actually binding is a fairly common thing in languages, like take the whole prolog or ml style languages, or haskell or many others too, even Rust and Kotlin and Scala and more, like here is OCaml ((*...*)
is a comment):
let a_binding = "Hi" in (* bind a_binding to string *)
let a () = print_endline a_binding in
let a_variable = ref "Bye" in (* make string variable bound to name a_variable *)
let b () = print_endline !a_variable in
let () = a () in (* print "Hi" *)
let () = b () in (* print "Bye" *)
let a_binding = "Yo" in (* rebind a_binding, like in elixir *)
let () = a_variable := "Vwoop" in (* Redefine the variable pointed to by name a_variable *)
let () = a () in (* print "Hi", no change from prior *)
let () = b () in (* print "Vwoop" *)
let a_variable = ref "Blorp" in (* Rebind a_variable *)
let () = b () in (* print "Vwoop", notice it used the old binding to the variable *)
()
And running that in the ocaml REPL outputs:
Warning 26: unused variable a_binding.
Warning 26: unused variable a_variable.
Hi
Bye
Hi
Vwoop
Vwoop
- : unit = ()
Languages with just variables without bindings conflate them together. C++ differs them the other way (something like int blah
makes a variable and const int blah
makes a binding). Rust does it like OCaml but without needing the variable setter syntax so let x = 42
is a binding and let mut x = 42
is a variable. Etc… etc… etc…
In the Elixir world an OCaml style ‘variable’ would probably just be an Agent:
a_binding = "Hi"
a = fn -> IO.puts(a_binding) end
a_variable = Agent.start_link(fn -> "Bye" end)
b = fn -> IO.puts(Agent.get(a_variable, & &1)) end
a.() # Hi
b.() # Bye
a_binding = "Yo"
Agent.update(a_variable, fn _ -> "Vwoop" end)
a.() # Hi
b.() # Vwoop
a_variable = Agent.start_link(fn -> "Blorp" end)
b.() # Vwoop
Which in iex does:
╰─➤ iex
Erlang/OTP 21 [erts-10.1.1] [source] [64-bit] [smp:6:6] [ds:6:6:10] [async-threads:1] [hipe]
Interactive Elixir (1.7.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> a_binding = "Hi"
"Hi"
iex(2)> a = fn -> IO.puts(a_binding) end
#Function<20.128620087/0 in :erl_eval.expr/5>
iex(3)> {:ok, a_variable} = Agent.start_link(fn -> "Bye" end)
{:ok, #PID<0.109.0>}
iex(4)> b = fn -> IO.puts(Agent.get(a_variable, & &1)) end
#Function<20.128620087/0 in :erl_eval.expr/5>
iex(5)> a.() # Hi
Hi
:ok
iex(6)> b.() # Bye
Bye
:ok
iex(7)> a_binding = "Yo"
"Yo"
iex(8)> Agent.update(a_variable, fn _ -> "Vwoop" end)
:ok
iex(9)> a.() # Hi
Hi
:ok
iex(10)> b.() # Vwoop
Vwoop
:ok
iex(11)> {:ok, a_variable} = Agent.start_link(fn -> "Blorp" end)
{:ok, #PID<0.118.0>}
iex(12)> b.() # Vwoop
Vwoop
:ok
So as you can see the binding/variable distinction is actually pretty common in languages, some focus more on the binding (elixir, ML’s, Haskell, Rust, Scala, Kotlin, etc…) and some focus more on the variable (C++, Java, Ruby, etc…), and that choice often drives a lot of the language design otherwise. Overall, and the reason why most ‘new’ languages are choosing to focus on the bindings, is that bindings tend to be a LOT more maintainable and easier to reason about as you always know it’s value and that it won’t ever change once set.