Question

I have a map such as this (which can be nested using the usual boolean operators, using module "boolean" and "data" would contain appropriate values, such as "left", "right" and "operator" would be "and", "or", "not"):

{ "data"   { "name" "lang", "operator" "=", "value" "blue" },
  "module" "impression_hint"}

What I want is a function that returns a list which can be interpolated in a (fn [context] ...) list, where it will be able to execute against what we'll throw at it.

Given the above structure, the final result, after being interpolated into a function, should be something like this:

(fn [context]
    (= ((context :impression_hint) "lang") "blue"))

Thus, my parsing/compiling function must only return the (= ...) part. I have something which is tantalizingly close:

(defn- parse-internal [tree acc]
  (cond
    ; other cases will go here

    (= "impression_hint" (tree "module"))
    (let [data  (tree "data")
          op    (data "operator")
          name  (data "name")
          value (data "value")]
      `(~(symbol op) ((context :impression_hint) ~name) ~value))

    :else
    (throw (RuntimeException. (str "Unknown module: " (tree "module"))))))

In my test, this returns:

FAIL in (simple-shallow-rules-generate-simple-shallow-functions) (targeting.clj:10)
expected: (= (quote (= ((context :impression_hint) "name") "blue")) (bloom.adgear.targeting/parse {"data" {"name" "lang", "operator" "=", "value" "blue"}, "module" "impression_hint"}))
  actual: (not (= (= ((context :impression_hint) "lang") "blue")
                  (= ((bloom.adgear.targeting/context :impression_hint) "lang") "blue")))

Notice the context parameter? It's been namespaced, and this is what is making my test fail. I'm certain I can do something, but calling symbol results in a symbol not defined, which makes sense since the symbol doesn't exist yet.

Regarding the acc parameter: I suspect I'll remove recursion from this function, eventually, and recur using acc, cons'ing my return value onto the accumulator. That's a bit further down the road though.

Was it helpful?

Solution

The problem is that namespace-less literal symbols in syntax-quoted forms (those preceded with a backtick) get the namespace component filled in based on where the syntax-quoted form lexically appears. Yours would appear to occur in the file where the bloom.adgear.targeting namespace is defined, so that's what gets attached to context.

Most of the time, this is a cool feature, but when you need to avoid it, you can use the ~' trick:

`foo
; => some-ns/foo
`~'foo
; => foo

The tilde unquotes the next form, so it isn't affected by syntax-quote's magic (including autoresolving symbols); but then it gets evaluated, so the quote is needed so you get back your original form (the symbol foo) and not its value (whatever foo is bound to currently).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top