Skip to content

Instantly share code, notes, and snippets.

@tdantas
Forked from reborg/vars.clj
Created Dec 4, 2017
Embed
What would you like to do?
;; (defn full-name ...) is equivalent to:
;; (def full-name (fn []...)) which defines a Var
;; object called full-name pointing at a compiled function
;; returning the static string "Oliv"
(defn full-name [] "Oliv")
;; greetings is a function taking two args.
;; it returns a function of no argument
;; invoking the second argument (f) with no arguments
;; and combining a string with it.
;; Like before, greetings is a Var object whose value
;; is a compiled function. This function has an instance variable
;; called "f". When .invoke() is called on the compiled function
;; greetings, the instance "f" variable is invoked.
(defn greetings [honorific f]
(fn [] (str honorific " " (f))))
;; Here we have a Var mr-name whose value is the result of
;; evaluating the expression on the right. The VarExpr on the right
;; is eventually invoked (because it's in parenthesis).
;; When greetings is evaluated results in a fn object which is invoked
;; with two arguments. Each argument is analyzed and evaluated, left to right.
;; The first argument is a string literal, evaluated as Java String.intern of
;; the string, returing the string itself. The second argument is a symbol.
;; The symbol is analyzed and a VarExpression is the result. The VarExpression
;; is then evaluated, which means to just "deref()" the var. The var value
;; is a function object. So the Var mr-name is assigned the value obtained by
;; invoking the fn object greetings using a string literal and another function
;; object as second argument. greetings invocation results in a function object
;; of no arguments (see definition above). This final object hasn't been
;; invoked yet.
(def mr-name (greetings "Mr." full-name))
;; Similar to the process seen above. There is one difference tho, which is
;; that #'full-name analysis does not result in a VarExpr being dereffed into
;; a function object. #'full-name evaluation result in the Var object full-name
;; itself. So sr-name is a Var object whose value is the result of evaluating
;; the greetings function with a string literal and a Var object (note, note
;; function object but a Var object.) The function object returned by greetings
;; has therefore an instance variable "f" which is a Var object.
(def sr-name (greetings "Sr." #'full-name))
;; mr-name is requested evaluation. This is the result of making access to the
;; local "f" which is a function object and invoke that without arguments.
;; The "f" function object that was passed when the function object was created
;; was the result of the evaluation of another function object full-name.
;; At that point in time, full-name was a function returning "Oliv"
(println (mr-name))
;; sr-name is requested evaluation. Almost the same as above, but when it's time
;; to see what (f) evaluates to, instead of calling a function object, we call
;; a Var instance as a function (which you can do, because it implements IFn)
;; When a Var object is invoked, it delegates the call to its value. The Var object
;; called "full-name" delegates the call to the function object generated by evaluating
;; the body of the function "full-name".
(println (sr-name))
;; This throws away the root binding of the full-name Var, replacing it with a new
;; function object that when invoked returns "Olivia" instead of "Oliv".
(defn full-name [] "Olivia")
;; This expression isn't changed. When it was compiled before, all evaluations
;; resulting in an invokable function object have been captured in their current
;; state at the time.
(println (mr-name))
;; This expression results now in a different output. The reason is the Var object
;; stored in the anonymous fn lambda that "greetings" returns. When the invocation
;; comes the Var object is invoked and since we had a chance to change it
;; in transit, we now access the new function body evaluation returning a
;; different string.
(println (sr-name))
;; Important take aways.
;; Both a Var object and a function object (resulting from evaluating a
;; fn) can be invoked. The Var object looks up its value and delegates the call.
;; All the following, assuming there are no external thread changing the root-value
;; of the var, are equivalent. But some invokes a compiled class created on the fly
;; by the clojure compiler when the function was evaluated, others are passing
;; through a Var middleman to achieve the same:
(full-name) ;; fn
(.invoke full-name) ;; fn
(#'full-name) ;; var->fn
(var full-name) ;; var->fn
(.invoke #'full-name) ;; var->fn
(.invoke (ns-resolve 'user 'full-name)) ;; var->fn
;; Since the Var looks up its corrent value at run-time, invoking the Var
;; introduces a layer of indirection, which allows the swap of the value
;; of the Var to be visible if a previous invocation captured the function object
;; instead of the var object. The following also shows the fact that "defn" returns
;; the Var object that is defining (or redefining). WARNING: this should never appear
;; as production code, because the let block is side-effecting the change to full-name
;; to the outside world in a block where it appears to be a local change.
(let [old (full-name)
over (defn full-name [] "something")]
[old (over)])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment