Created
November 13, 2017 17:26
-
-
Save reborg/401dc12e59a514cbd5bcdff9ca2dbfa0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; (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