Fuzzy construction in contextual eval in Clojure "Joy"

The following code is from chapter 8.1.1 (second edition) "Joy" by Clojure from Fogus, Houser:

(defn contextual-eval [ctx expr] (eval `(let [ ~@ (mapcat (fn [[kv]] [k `'~v]) ctx)] ; Build let bindings at compile time ~expr))) (contextual-eval '{a 1, b 2} '(+ ab)) ;;=> 3 (contextual-eval '{a 1, b 2} '(let [b 1000] (+ ab))) ;;=> 1001 

I do not understand the meaning of the `'~v . Can anyone think of this?

The book says that

The generated bindings use the interesting pattern `` ~ v to get the value of the built-in binding at run time.

for instance

 (contextual-eval '{a 1, b 2} '(+ ab)) 

expands to

 (let [a '1 b '2] (+ ab))) 

and I don’t understand why these quotes are entered, what are they good for.

In addition, we have the following behavior:

 (contextual-eval '{a 1, b (+ a 1)} '(+ ab)) ClassCastException clojure.lang.PersistentList cannot be cast to java.lang.Number (defn contextual-eval' [ctx expr] (eval `(let [ ~@ (mapcat (fn [[kv]] [kv]) ctx)] ~expr))) (contextual-eval' '{a 1, b (+ a 1)} '(+ ab)) ;=> 3 
+5
source share
1 answer

This expression uses almost all the special noise-like characters available in Clojure, so you should highlight it:

  • ` is a reader macro for" quote syntax "
    "Syntax-quote" is a feature among reader macros because you can only call this function through a short form. You cannot, for example, call something like (syntax-quote something-here ) , instead write `something-here . It provides a rich set of options for determining which parts of an expression should be evaluated after it, and which should be taken literally.
  • ' is a read-macro shortcut for the special quote form. This leads to the fact that the expression that it wraps should not be evaluated, and should instead be treated as data. If you want to write a quote literal without evaluating it, you can write `'something to get `(quote something) as the result. And this will lead to the fact that the resulting quote expression will not be evaluated, it just returned while it was not running yet.

  • ~ is part of the syntax quote syntax (this is a “quote” with the syntax), which means “actually allow this part to work,” so if you have a large list that you want to take literally (don't start right now), except that you have one element that you really want to evaluate right now, then you can write `(abc ~defg) , and d will be the only thing on this list that will be evaluated according to what is currently defined.

So now we can put it all together:

`'~ means "make a quote expression that contains the value v, as it is right now"

 user> (def v 4) #'user/v user> `'~v (quote 4) 

And the motivation for this fantasy:

 (contextual-eval '{a 1, b 2} '(+ ab)) 

seems like just adding some extra thinking without any benefit, because it basically just quotes the values ​​1 and 2. Since these are the correct “values,” they never change.

Now, if the expression was instead:

 (contextual-eval '{a (slurp "https://example.com/launch?getCode") b the-big-red-button} '(press ba)) 

Then it would be wiser to be careful when this particular bit of code starts up. Thus, this pattern is associated with controlling what phase of the program’s life actually runs the code. Clojure has several "times" when code can work:

  • during macro assessment: during code generation. (side effects here require a lot of forethought).
  • when namespaces are loaded: this is when forms run at the top level. This often happens when the program starts and before calling main .
  • things that start as a result of starting main

ps: the above definitions are adapted to the context of this question and are not intended to use "official" terms.

+7
source

Source: https://habr.com/ru/post/1241620/


All Articles