Even though I have used Clojure, I hadn't looked at the scoping rules in detail. I am getting more confused as I read the documentations.
I made a small test to try out the scoping resolutions and am apalled at the complexity. Could somebody explain the intent and various rules that Clojure uses?
(def x 1)
(defn dummy-fn2[]
(+ x 1))
(defn dummy-fn[]
(println "entering function: " x)
(let [x 100]
(println "after let: " x)
(let [x (dummy-fn2)]
(println "after let and dummy2: " x)
(binding [x 100]
(println "after binding: " x)
(let [x (dummy-fn2)]
(println "after binding and dummy2: " x))))))
1:2 foo=> (dummy-fn)
entering function: 1
after let: 100
after let and dummy2: 2
after binding: 2
after binding and dummy2: 101
nil
let shadows the toplevel Var x with a local x. let does not create a Var or affect the toplevel Var; it binds some symbol such that local references to that symbol will be replaced with the let-bound value. let has lexical scope, so its bindings are only visible within the let form itself (not in functions called from within the let).
binding temporarily (thread-locally) changes the value of the toplevel Var x, that's all it does. If a let binding is in place, binding doesn't see it when deciding which value to change (and let's bindings are not vars and are not alterable, so that' a good thing or it'd give you an error). And binding won't mask let. binding has dynamic scope, so its affects on toplevel Vars are visible within the binding form and in any function that is called from within the binding form.
Accessing the value of plain old x will give you whatever is at the top of the stack of bindings, either the most nested let-bound value of x (or the function paramater called x, or some value x is replaced with if you use your own macro, or other possibilities.), and only uses the current value of the toplevel Var x by default if there is no other binding in place.
Even if the toplevel Var x is masked by a let-bound x, you can always access the toplevel Var via ##'x. Try this version, maybe it'll make more sense:
(def x 1)
(defn dummy-fn2[]
(println "x from dummy-fn2:" x)
(+ x 1))
(defn dummy-fn[]
(println "entering function:" x)
(println "var x:" ##'x)
(dummy-fn2)
(println "---")
(let [x 100]
(println "after let:" x)
(println "var x:" ##'x)
(dummy-fn2)
(println "---")
(let [x (dummy-fn2)]
(println "after let and dummy-fn2:" x)
(println "var x:" ##'x)
(dummy-fn2)
(println "---")
(binding [x 888]
(println "after binding:" x)
(println "var x:" ##'x)
(dummy-fn2)
(println "---")
(let [x (dummy-fn2)]
(println "after binding and dummy2:" x)
(println "var x:" ##'x)
(dummy-fn2)
(println "---"))))))
Gives:
entering function: 1
var x: 1
x from dummy-fn2: 1
---
after let: 100
var x: 1
x from dummy-fn2: 1
---
x from dummy-fn2: 1
after let and dummy-fn2: 2
var x: 1
x from dummy-fn2: 1
---
after binding: 2
var x: 888
x from dummy-fn2: 888
---
x from dummy-fn2: 888
after binding and dummy2: 889
var x: 888
x from dummy-fn2: 888
---
Clojure uses both lexical scope let for symbols and dynamic scope binding for vars
check out clojure's vars documentation.
"entering function": doing fine so far! the symbol x resolves to the var and this is grabbing the "root binding" of the var x.
"after let": a local binding covered up the var, the symbol x is now 100 not a var.
"after let and dummy2": the x in dummy-fn2 refers to the var x, so it uses the root binding of x and returned one more than that (+ 1 1)
"after binding": tricky one! binding dynamically replaced the root binding of the var named x (which was 1) with 100, but the local symbol x is not the var anymore so you get the local binding.
"after binding and dummy2": the binding replaced the root value of the var x with 100 and this returned one more than that (+ 100 1)
Related
If the variables are immutable in Clojure, then why did it allow to redefine the same variable in the following snippet:
(ns tutorial.test-immutability-of-variables)
(defn test_if_variables_are_mutable
[]
(def a 1)
(def a 10)
(println (format "Value of a: %d" a) )
)
(test_if_variables_are_mutable)
The above code compiles and o/p is 10.
Clojure makes a distinction between values and references to values. Clojure guarantees that values are immutable.
Consider what happens when your function is evaluated:
(def a 1)
When the above form is evaluated, a Clojure Var is created (with name a and namespace tutorial.test-immutability-of-variables) and it is bound to the value 1.
(def a 10)
When this form is evaluated, the existing Clojure Var is rebound to the value 10.
Use of def within a function is strongly discouraged. Use def for top level forms only in your code. Alternatively for a lexically bound symbol use let.
The full story of what happens is a bit more involved than I've outlined above, see the reference documentation on Vars. There are other reference types in Clojure, such as Atoms.
As the other answer points out, you can change what the global Var points to, but you cannot change the original value. Here is another example:
(def x 42)
(defn changer-1
[]
(println "changer-1: x - old" x)
(let [x "Goodbye"] ; change a local variable
(println "changer-1: x - new" x)))
(defn caller-1
[]
(newline)
(let [x "Hello"]
(println "caller-1: x - before" x)
(changer-1)
(println "caller-1: x - after " x)))
(defn changer-2
[]
(println "changer-2: x - old" x)
(def x 666) ; reset the global Var (normally bad form)
(println "changer-2: x - new" x))
(defn caller-2
[]
(newline)
(println "caller-2: Var x - before" x)
(let [x "Hello"]
(println "caller-2: x - before" x)
(changer-2)
(println "caller-2: x - after " x))
(println "caller-2: Var x - after" x))
(caller-1)
(caller-2)
with result
-----------------------------------
Clojure 1.10.3 Java 15.0.2
-----------------------------------
caller-1: x - before Hello
changer-1: x - old 42
changer-1: x - new Goodbye
caller-1: x - after Hello
caller-2: Var x - before 42
caller-2: x - before Hello
changer-2: x - old 42
changer-2: x - new 666
caller-2: x - after Hello
caller-2: Var x - after 666
For more information, please see this list of documentation sources, especially "Getting Clojure" and "Brave Clojure"
Until now, I had always assumed that anything you can do in let binding, you can do within the arguments vector for a defn form.
However, I just noticed this -- if I do this with a let binding, it works:
(let [[x & more :as full-list] (range 10)]
(println "x:" x)
(println "more:" more)
(println "full list:" full-list))
; x: 0
; more: (1 2 3 4 5 6 7 8 9)
; full list: (0 1 2 3 4 5 6 7 8 9)
But if I try to pull it out into a function, I get an exception:
(defn foo [x & more :as full-list]
(println "x:" x)
(println "more:" more)
(println "full list:" full-list))
; CompilerException java.lang.RuntimeException: Unexpected parameter, compiling:(/tmp/form-init615613631940782255.clj:1:1)
Of note, this works:
(defn foo [[x & more :as full-list]]
(println "x:" x)
(println "more:" more)
(println "full list:" full-list))
But then I have to pass in the argument as a collection, i.e. (foo [1 2 3]).
Is it possible to define a function that takes a variable number of arguments, and bind the entire group of arguments to a local variable, without specifically using a let binding inside? It strikes me as weird that you can't just do (defn foo [x & more :as full-list] ... Is there a particular reason why this doesn't (or shouldn't) work?
If you want a variable number of args, you are missing an &:
(defn foo [& [x & more :as full-list]]
(println "x:" x)
(println "more:" more)
(println "full list:" full-list))
Clojure param definition has only one special case that is the & char to indicate a variadic number of arguments. The rest are plain simple named arguments.
Now each simple argument can be destructured using the map or list syntax. For example:
(defn foo [ x y ] ...)
Can be destructured like:
(defn foo [[x1 x2 & x-more :as x] {:keys [y1 y2 y3]}] ...)
So we are saying that we expect the first param to be a list of at least 2 elements and the second param to be a map with some keys. Note that this will still be a fn of two params and that Clojure is not going to enforce that x actually has at least two elements. If x is an empty list, x1 and x2 will be nil.
Coming back to your question, if you look at my answer you will see that my fn has 0 mandatory params, with a variable number of arguments, while the one that you have has 1 mandatory param with a a variable number of arguments. What I am doing is just destructuring the var arg.
What's the difference between using "def" to update a var and using "alter-var-root"?
e.g.
(def x 3)
(def x (inc x))
vs
(def x 3)
(alter-var-root #'x inc)
I find alter-var-root very rarely comes up in idiomatic Clojure code; not that there is anything wrong with it, it's just intended for corner cases. If you find yourself using it to build loops and such it's a sign something needs a different approach. I mostly see it in initialization routines for setting access credentials or loggers and such.
alter-var-root uses a function to mechanically change the value of a var while def just sets it to a new value. In your example they are equivalent.
hello.exp> (def foo 4)
#'hello.exp/foo
hello.exp> (alter-var-root #'foo inc)
5
hello.exp> foo
5
alter-var-root is also unwilling to create a new var:
hello.exp> (alter-var-root #'foo1 inc)
CompilerException java.lang.RuntimeException: Unable to resolve var: foo1 in this context, compiling:(NO_SOURCE_PATH:1)
alter-var-root can work on other namespaces as well:
hello.exp> (in-ns 'user)
#<Namespace user>
user> (alter-var-root #'hello.exp/foo inc)
6
user> (def hello.exp/foo 4)
CompilerException java.lang.RuntimeException: Can't create defs outside of current ns, compiling:(NO_SOURCE_PATH:1)
user>
This last use case is the only one I have ever needed in practice. For instance forcing clojure.logging to use the correct slf4j logger as an example from the Pallet project:
(defn force-slf4j
"The repl task brings in commons-logging, which messes up our logging
configuration. This is an attempt to restore sanity."
[]
(binding [*ns* (the-ns 'clojure.tools.logging.slf4j)]
(alter-var-root
#'clojure.tools.logging/*logger-factory*
(constantly (clojure.tools.logging.slf4j/load-factory)))))
Which is just using alter-var-root to reset a var in another namespace regardless of its content on initialization. I suppose it's a bit of a hack ...
alter-var-root provides the added value of being atomic with regards to the function application. Two (possibly concurrent) applications of (alter-var-root #'foo inc) guarantee that foo will increase by 2.
With (def x (inc x)) there is no such guarantee. It might overwrite any changes done by other threads between reading the value of x and writing its updated value.
On the other hand, if you are using alter-var-root for its atomicity then perhaps atoms are better for your use case than vars.
With def:
(def w (vector)) ; create Var named w and bind it to an empty vector
(dotimes [x 9] ; repeat 9 times (keeping iteration number in x):
(future ; execute in other thread:
(def w ; replace root binding of w with
(conj w ; a new vector with all elements from previous (w)
x)))) ; with added an element indicating current iteration (x)
w ; get a value of Var's root binding (identified by symbol w)
; => [0 2 3 6 8 7 4 5] ; 1 is missing !!!
; second thread overlapped with another thread
; during read-conjoin-update and the other thread "won"
With alter-var-root:
(def w (vector)) ; create Var named w and bind it to an empty vector
(dotimes [x 9] ; repeat 9 times (keeping iteration number in x):
(future ; execute in other thread:
(alter-var-root #'w ; atomically alter root binding of w
(fn [old] ; by applying the result of a function,
(conj ; that returns a new vector
old ; containing all elements from previous (w)
x))))) ; with added an element indicating current iteration (x)
w ; get a value of Var's root binding (identified by symbol w)
; => [1 2 4 5 3 0 7 8 6]
I would like to know how to increment by X amount a number, in other languages I used to do
foo += 0.1;
but I have not idea how it can be done in Clojure
Variables are immutable in Clojure. So you should not try to change the value of foo, but instead, "create" a new foo:
(def foo2 (+ foo 0.1))
...or, if in a loop, recur with a new value:
(loop [foo 5.0]
(when (< foo 9)
(recur (+ foo 0.1))))
...or, if foo is an atom, swap! it with a new value:
(def foo (atom 5.0))
(swap! foo (partial + 0.1))
I recommend you start by reading the rationale of Clojure.
Blacksad's answer covers defining vars so I would just like to add the other scopes in which you may wish to have a value that is incremented from another value:
within a function's local scope:
user> (defn my-function [x]
(let [y (inc x)
z (+ x y)]
[x y z]))
#'user/my-function
user> (my-function 4)
[4 5 9]
and If you want to build a value incrementally to make the process more clear:
user> (defn my-function [x]
(let [y (inc x)
z (+ x y)
z (+ z 4)
z (* z z)]
[x y z]))
#'user/my-function
user> (my-function 4)
[4 5 169]
This can make the process more presentable, though it is not a "way to get variables back" and is really only useful in limited contexts. This pattern is used in clojure.core's threading macros.
I have the following defined in clojure:
(def ax '(fn x [] (+ 1 z)))
(let [z 4]
(str (eval ax))
)
:but instead of returning :
5
: I get :
Unable to resolve symbol: z in this context
: I have tried changing "let" to "binding" but this still does not work. Does anyone know what is wrong here?
Making the smallest possible changes to your code to get it to work:
(def ^:dynamic z nil)
(def ax '(fn x [] (+ 1 z)))
(binding [z 4]
(str ((eval ax)))
)
The two changes are defining z as a dynamic var, so that the name resolves, and putting another paren around (eval ax), because ax is returning a function.
A little bit nicer is to change the definition of ax:
(def ^:dynamic z nil)
(def ax '(+ 1 z))
(binding [z 4]
(str (eval ax))
)
So evaluating ax immediately gets the result you want, rather than returning a function that does it.
Nicer again is to skip the eval:
(def ^:dynamic z nil)
(defn ax [] (+ 1 z))
(binding [z 5]
(str (ax))
)
But best of all is to not have z floating around as a var, and pass it in to ax as Mimsbrunnr and Joost suggested.
The short answer is don't use eval. You almost never need to, and certainly not here.
For example:
user> (defn ax [z]
(+ 1 z))
#'user/ax
user> (let [f #(ax 4)]
(f))
5
Right so I'm not entirely sure what you are trying to do here.
I mean this works, though it's not using eval it's defining x to be the function (fn [ x ] (+ x 1))
> (def x #(+ 1 %))
#'sandbox102711/x
> (x 4)
5
In the end, eval is not something you should be using. As a Lisp Cljoure's support for lambda abstraction and macros ( see the fn definition above ) should remove the need.