Fork me on GitHub
#beginners
<
2022-11-17
>
bschrag04:11:51

Given that the form using let and macroexpand directly below macroexpands just fine, can anyone explain why the analogous definition following that form errors out during macro expansion? exorcise.core> (let [term-string "a person"] (macroexpand (tm/with-matching-template-symbols ["a *type"` ,term-string] (declare-inferred-type *type)))) (let* [*type 'person] (exorcise.core/declare-inferred-type exorcise.core/*type)) exorcise.core> (defn foo [term-string] (tm/with-matching-template-symbols ["a *type" term-string] (declare-inferred-type *type))) Unexpected error (NoSuchMethodException) macroexpanding tm/with-matching-template-symbols at (*cider-repl Cogex/exorcise:localhost:45999(clj)*:112:18). exorcise.core$foo$eval6498__6499.<init>() exorcise.core>

bschrag04:11:36

FWIW, here are relevant definitions. (defn tokens->quoted-symbol [string] (list 'quote (read-string (clojure.string/replace string " " "_")))) ;;; When you want symbols, it can be convenient to get them directly. ;;; (If you want a mixture of strings and symbols, use both the macro and function above.) (defmacro with-matching-template-symbols [[template input-form] & body] (let [bindings-hashmap (eval (match-template template input-form))]` (when-not (= bindings-hashmap :fail) (let [symbols-hashmap (zipmap (keys bindings-hashmap) (map tokens->quoted-symbol (vals bindings-hashmap))) bindings-list (into [] (apply concat ; flatten goes to far here, destroys quoting. (into [] symbols-hashmap)))] (let ~bindings-list` ;; Evaluate the body. ~@body)))))

rolt13:11:34

you're doing (eval (match-template template input-form))` when macroexpanding. In you failing example term-string has no value, so I don't know what ~input-form could look like

Ed13:11:55

that's not the error I get when I try and run the code you've pasted here. The first error I get is

No such var: macro/term-string
(I'm evaling it in a ns called macro). I can fix that by quoting term-string in the macro expand call.
(let [term-string "a person"]
    (macroexpand `(with-matching-template-symbols ["a *type" 'term-string]
                    (declare-inferred-type *type))))
I then didn't have a definition for match-template so I defined this
(defn match-template [a b] {'*type 'person})
The error seems to be coming from your call to eval which I'm not sure I understand why it's there. It looks like it's trying to call match-template to get some information at macroexpansion time? If that's true then changing the code to just calling the function makes the problem go away.
(defmacro with-matching-template-symbols [[template input-form]
                                          & body]
  (let [bindings-hashmap (match-template template input-form)]
    (when-not (= bindings-hashmap :fail)
      (let [symbols-hashmap (update-vals bindings-hashmap tokens->quoted-symbol)
            bindings-list (into [] cat symbols-hashmap)]
        `(let ~bindings-list
           ;; Evaluate the body.
           ~@body)))))
also, I tidied up a couple of bits that looked weird.

bschrag18:11:32

@l0st3d Losing the (leftover/crufty/embarrassing) eval gets past the error I'd reported. And thanks for the instructive tidying up. Now I see a different error where I try to use the macro below that calls the improved macro above. The intended binding seems inaccessible. exorcise.core> *(defmacro process-variable-term [term-string]* ``(tm/with-matching-template-symbols ["a *type"` `~term-string]` `(declare-inferred-type *type)))` #'exorcise.core/process-variable-term exorcise.core> (process-variable-term "a person") Syntax error compiling at (*cider-repl Cogex/exorcise:localhost:44009(clj)*:177:16). No such var: exorcise.core/*type exorcise.core> I'm now wondering if I've just brought too many macro expectations from Common Lisp, where this (e.g.) works: cl-user(20): *(defmacro with-bindings-list (bindings-list &body body)* ``(let ,bindings-list` `,@body))` with-bindings-list cl-user(21): *(with-bindings-list ((x 1)) x)* 1 cl-user(22): *(setf the-bindings '((x 2)))* ((x 2)) cl-user(23): *(eval (with-bindings-list ,the-bindings x))*` 2 cl-user(24): Apparently Clojure treats the final analogous form below differently. exorcise.core> *(defmacro with-bindings-list [bindings-list & body]* ``(let ~bindings-list` `~@body))` #'exorcise.core/with-bindings-list exorcise.core> *(with-bindings-list [x 1] x)* 1 exorcise.core> *(def the-bindings '[x 2])* #'exorcise.core/the-bindings exorcise.core> *(eval (with-bindings-list ~the-bindings x))*` Syntax error compiling at (*cider-repl Cogex/exorcise:localhost:44009(clj)*:169:16). No such var: exorcise.core/x exorcise.core> I've played around with different combinations of qualified vs. simple x for occurrences in (with-bindings-list [x 1] x), without success. At this point, an example might help me build confidence. Are you familiar with any Clojure macro (like my with-matching-template-symbols) that will establish a local binding for a symbol (like my term-string) passed via a parameter (like my input-form) to the macro's calling function (like my process-variable-term)?

seancorfield18:11:06

@U65G9MPCK using triple backticks around code makes it a lot easier to read than however you are adding code into your posts right now:

exorcise.core> (defmacro with-bindings-list [bindings-list & body]
	              `(let ~bindings-list
		             ~@body))
#'exorcise.core/with-bindings-list
exorcise.core> (with-bindings-list [x 1] x)
1
exorcise.core> (def the-bindings '[x 2])
#'exorcise.core/the-bindings
exorcise.core> (eval `(with-bindings-list ~the-bindings x))
Syntax error compiling at (*cider-repl Cogex/exorcise:localhost:44009(clj)*:169:16).
No such var: exorcise.core/x
exorcise.core>
(also, please don't post walls of text/code to the main channel -- keep it in a thread)

👍 1
😳 1
hiredman18:11:11

synxtax quote (`) namespaces symbols

hiredman18:11:19

which is the dfiference

hiredman18:11:44

so the final x in your call to eval is actually exorcise.core/x

hiredman18:11:59

which doesn't match the let bound x

hiredman18:11:26

which is why you get the error about no such var exorcise.core/x

Alex Miller (Clojure team)18:11:10

this is a case where it might actually be easier to construct the output with list rather than using `

hiredman19:11:01

yeah, if you constructed the form passed to eval in a way that didn't namespace qualify symbols automatically it would work

hiredman19:11:20

doing it manually with list is one way to do that

hiredman19:11:33

you can also do you things like ~'x

bschrag19:11:36

@U0NCTKEV8 @U064X3EF3 So, this is nice.

exorcise.core> (eval `(with-bindings-list ~the-bindings ~'x))
2
Attempting to apply the same guidance to my function, below, ...
(defmacro with-matching-template-symbols [[template input-form]
                                          & body]
  (let [bindings-hashmap (match-template template input-form)]
    (when-not (= bindings-hashmap :fail)
      (let [symbols-hashmap (update-vals bindings-hashmap tokens->quoted-symbol)
            bindings-list (into [] cat symbols-hashmap)]
        ;; Syntax quote (`) namespaces symbols, which we don't want.
        ;; `(let ~bindings-list
        ;;   ~@body)
        (cons 'let
              (cons bindings-list
                    body))))))
...I still have trouble. REPL message on C-M-X in Cider:
Syntax error macroexpanding with-matching-template-symbols at (*cider-repl Cogex/template-matcher:localhost:45323(clj)*:1060:1).
Don't know how to create ISeq from: clojure.lang.Symbol
From the backtrace (below), it looks like the compiler is pushing through the body's full execution trace, then complaining about a symbol where it wants a string.
2. Unhandled clojure.lang.Compiler$CompilerException
   Error compiling *cider-repl Cogex/template-matcher:localhost:45323(clj)* at (1060:1)
   #:clojure.error{:phase :macro-syntax-check,
                   :line 1060,
                   :column 1,
                   :source
                   "*cider-repl Cogex/template-matcher:localhost:45323(clj)*",
                   :symbol with-matching-template-symbols}
             Compiler.java: 7027  clojure.lang.Compiler/macroexpand1
             Compiler.java: 7110  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6806  clojure.lang.Compiler/analyze
             Compiler.java: 6762  clojure.lang.Compiler/analyze
             Compiler.java: 6137  clojure.lang.Compiler$BodyExpr$Parser/parse
             Compiler.java: 5479  clojure.lang.Compiler$FnMethod/parse
             Compiler.java: 4041  clojure.lang.Compiler$FnExpr/parse
             Compiler.java: 7122  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6806  clojure.lang.Compiler/analyze
             Compiler.java: 7112  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6806  clojure.lang.Compiler/analyze
             Compiler.java:   38  clojure.lang.Compiler/access$300
             Compiler.java:  596  clojure.lang.Compiler$DefExpr$Parser/parse
             Compiler.java: 7124  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6806  clojure.lang.Compiler/analyze
             Compiler.java: 6762  clojure.lang.Compiler/analyze
             Compiler.java: 7198  clojure.lang.Compiler/eval
             Compiler.java: 7149  clojure.lang.Compiler/eval
                  core.clj: 3215  clojure.core/eval
                  core.clj: 3211  clojure.core/eval
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  667  clojure.core/apply
                  core.clj: 1990  clojure.core/with-bindings*
                  core.clj: 1990  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  152  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  218  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  217  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java:  833  java.lang.Thread/run

1. Caused by java.lang.IllegalArgumentException
   Don't know how to create ISeq from: clojure.lang.Symbol

                   RT.java:  557  clojure.lang.RT/seqFrom
                   RT.java:  537  clojure.lang.RT/seq
                  core.clj:  139  clojure.core/seq
                  core.clj:  139  clojure.core/seq
                  core.clj:   41  template-matcher.core/strip-leading-chars
                  core.clj:   39  template-matcher.core/strip-leading-chars
                  core.clj:   95  template-matcher.core/read-token
                  core.clj:   94  template-matcher.core/read-token
                  core.clj:  160  template-matcher.core/match-template
                  core.clj:  146  template-matcher.core/match-template
               RestFn.java:  425  clojure.lang.RestFn/invoke
                      REPL:  236  template-matcher.core/with-matching-template-symbols
                      REPL:  234  template-matcher.core/with-matching-template-symbols
               RestFn.java:  146  clojure.lang.RestFn/applyTo
                  Var.java:  705  clojure.lang.Var/applyTo
             Compiler.java: 7010  clojure.lang.Compiler/macroexpand1
             Compiler.java: 7110  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6806  clojure.lang.Compiler/analyze
             Compiler.java: 6762  clojure.lang.Compiler/analyze
             Compiler.java: 6137  clojure.lang.Compiler$BodyExpr$Parser/parse
             Compiler.java: 5479  clojure.lang.Compiler$FnMethod/parse
             Compiler.java: 4041  clojure.lang.Compiler$FnExpr/parse
             Compiler.java: 7122  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6806  clojure.lang.Compiler/analyze
             Compiler.java: 7112  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6806  clojure.lang.Compiler/analyze
             Compiler.java:   38  clojure.lang.Compiler/access$300
             Compiler.java:  596  clojure.lang.Compiler$DefExpr$Parser/parse
             Compiler.java: 7124  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6806  clojure.lang.Compiler/analyze
             Compiler.java: 6762  clojure.lang.Compiler/analyze
             Compiler.java: 7198  clojure.lang.Compiler/eval
             Compiler.java: 7149  clojure.lang.Compiler/eval
                  core.clj: 3215  clojure.core/eval
                  core.clj: 3211  clojure.core/eval
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  667  clojure.core/apply
                  core.clj: 1990  clojure.core/with-bindings*
                  core.clj: 1990  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  152  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  218  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  217  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java:  833  java.lang.Thread/run

Ed19:11:05

@U65G9MPCK Sorry, I've been afk. I think the error is in whatever match-template does. Specifically,

core.clj:   41  template-matcher.core/strip-leading-chars
I think you're passing a symbol to strip-leading-chars where maybe it wants a string? or at least something it's going to call seq on??

hiredman19:11:35

the syntax quote stuff won't apply above, syntax quote only namespace qualifies literal symbols

Alex Miller (Clojure team)19:11:41

if you're not already, you might find it helpful to examine the expansion with something like (pprint (macroexpand '(with-matching-template-symbols ...))

hiredman19:11:17

so for (foo ~@bar)` foo will be namespace qualified but any symbols within bar will not be

bschrag20:11:28

@U064X3EF3 The error comes during macro expansion, so I don't know how to test it that way. macroexpand-1 doesn't show much.

bschrag20:11:06

Ok, this works now:

(defmacro process-variable-term [term-string]
  (cons 'with-matching-template-symbols
        (cons ["a *type"
               term-string]
              '((declare-inferred-type *type)
                (type->variable *type)))))

👍 1
bschrag20:11:14

template-matcher.core> (process-variable-term "a person")
?person

bschrag20:11:11

Is there a non-namespacing version of syntax quote somewhere? I have a lot of macros to cons up, at this point. 🙂

Ed20:11:32

syntax quote will only namespace literals. So where you've written

(cons 'let
              (cons bindings-list
                    body))
you could write
`(let ~bindings-list
     ~@body)
it won't traverse the contents of bindings-list and namespace all the symbols it finds.

Ed20:11:28

So maybe you could write something like this?

(defmacro process-variable-term [term-string]
  `(with-matching-template-symbols ["a *type" ~term-string]
     (declare-inferred-type ~'*type)
     (type->variable ~'*type)))
In my time writing macros I've made the mistake of creating anaphoric macros like that, and ended up wishing I hadn't. It's usually better to be explicit about the names you're creating so you can avoid clashes.

bschrag21:11:42

@l0st3d Yes, that works also. I think I should be able to keep my code clean, now. 🙂 THANK YOU !!

zach05:11:11

Hello! Is there a simple way in clojure to group a sequence of strings by a separator? This feels like an easy clojure task, but my brain is failing me. For example, let’s say I have the sequence:

'("BEGIN" "a" "b" "c" "d" "END" "BEGIN" "foo" "bar" "END" "BEGIN" "c" "l" "j" "cool" "END")
And I would, ideally, group this into a sequence of three sequences:
'(("BEGIN" "a" "b" "c" "d" "END") ("BEGIN" "foo" "bar" "END") ("BEGIN" "c" "l" "j" "cool" "END"))
Is there a straightforward way to do this?

dumrat06:11:13

reduce is one way:

(second
 (reduce
  (fn [[curr so-far] s]
    (if (= s "END")
      [[] (conj so-far (conj curr s))]
      [(conj curr s) so-far]))
  [[] []]
  '("BEGIN" "a" "b" "c" "d" "END" "BEGIN" "foo" "bar" "END" "BEGIN" "c" "l" "j" "cool" "END")))

zach06:11:42

ah phenomenal, thank you!

dumrat06:11:38

@U011DMQ8DSS Pretty sure there must be a more idiomatic solution though. Perhaps someone else will pitch in.

dpsutton06:11:49

core=> (->> ["BEGIN" "a" "b" "c" "d" "END" "BEGIN" "foo" "bar" "END" "BEGIN" "c" "l" "j" "cool" "END"]
            (partition-by #{"END"})
            (partition-all 2)
            (map (partial apply concat)))
(("BEGIN" "a" "b" "c" "d" "END")
 ("BEGIN" "foo" "bar" "END")
 ("BEGIN" "c" "l" "j" "cool" "END"))

👍 2
zach06:11:45

Ah this is great, thank you too!

zach06:11:07

For the second example (and to betray my clojure newness): would (map (partial apply concat)) be equivalent to (map #(apply concat %))? Running through the code, it looks to give the same results, but I might be missing some nuance?

dpsutton06:11:43

in this instance yeah they would be equivalent

👍 1
tomd10:11:39

This question of taking/dropping/partitioning upto some condition comes up quite often, but often isn't intuitive to solve with core fns. I think https://github.com/weavejester/medley take-upto and drop-upto fns can solve this sort of problem quite nicely:

(->> ["BEGIN" "a" "b" "c" "d" "END" "BEGIN" "foo" "bar" "END" "BEGIN" "c" "l" "j" "cool" "END"]
     (iterate #(m/drop-upto #{"END"} %))
     (map     #(m/take-upto #{"END"} %))
     (take-while seq))
;; => (("BEGIN" "a" "b" "c" "d" "END") ("BEGIN" "foo" "bar" "END") ("BEGIN" "c" "l" "j" "cool" "END"))

skylize13:11:09

In @U11BV7MTK's solution, the case of the final group missing a closing tag raises an interesting and meaningful difference if you replace partition-all with partition.

(let [m ["BEGIN" "a" "b" "c" "d" "END"
         "BEGIN" "foo" "bar" "END"
         "BEGIN" "c" "l" "j" "cool"]
      group (fn [part-fn m]
              (->> m
                   (partition-by #{"END"})
                   (part-fn 2)
                   (map (partial apply concat))))]
  
  (prn (group partition-all m))
  ; => (("BEGIN" "a" "b" "c" "d" "END")
  ;      ("BEGIN" "foo" "bar" "END")
  ;      ("BEGIN" "c" "l" "j" "cool"))
  
  (prn (group partition m))
  ; => (("BEGIN" "a" "b" "c" "d" "END")
  ;      ("BEGIN" "foo" "bar" "END"))
  )

ghadi18:11:47

spec solution

user=> (require '[clojure.spec.alpha :as s])
nil
user=> (s/def ::group (s/cat :begin #{"BEGIN"} :body (s/+ string?)  :end #{"END"}))
:user/group
user=> (s/conform ::group ["BEGIN" "A" "B" "C" "END"])
{:begin "BEGIN", :body ["A" "B" "C"], :end "END"}

😍 1
dpsutton18:11:30

could that easily be extended to a s/coll-of solution to handle the original input? (forget how great the spec regex style stuff is)

ghadi18:11:47

would be an s/cat with a regex +

ghadi18:11:29

(s/coll-of would result in a collection of groups, but the original input in the question is "flat")

👍 1
quan xing11:11:22

(def session (atom {}))
(swap! session assoc 123 123)
; Execution error (ClassCastException) at app.freeotp/eval32622 (REPL:2268).
; clojure.lang.LazySeq cannot be cast to clojure.lang.Associative
why? show ClassCastException

dumrat11:11:57

Pretty sure you are storing something else to that atom somewhere

pithyless11:11:21

If you're not sure why this is happening, maybe try some puts debugging:

(swap! session #(do (prn %) (assoc % 123 123)))
At least you'll learn what's there instead of the expected map.

dumrat12:11:42

Or if you want to find out where the value comes from, I guess something like this?

(require '[clojure.stacktrace :as st])

(def a (atom {} :validator
             (fn [v]
               (st/print-stack-trace (Exception.))
               (println "New value for atom: " v)
               true)))

(defn test []
  (swap! a assoc 1 1))

(test)
;; Will print stacktrace here

Apple14:11:04

$ clj
Clojure 1.11.1
user=> (def s (atom {}))
#'user/s
user=> (swap! s assoc 123 123)

{123 123}
user=> user=> 
no problem here

quan xing01:11:52

Thanks everyone:+1:

pavlosmelissinos16:11:19

Can I safely replace #(clojure.pprint/pprint (with-out-str %)) with prn-str or can they give different results for a particular input? They seem to be interchangable but the implementations take different paths so I'm wondering if I'm missing something

Alex Miller (Clojure team)16:11:46

pprint will add new lines and respond to other pretty printing directives where prn will not

🙏 1
ghadi16:11:17

with-out-str is a macro, can't comp it

ghadi16:11:53

(i assume you meant to comp them "in spirit")

👍 1
pavlosmelissinos16:11:13

This particular case doesn't define any directives but I didn't think about the new lines, thanks 🙂 > with-out-str is a macro, can't comp it I didn't mean a literal comp, yes, but good point, especially since we're in #C053AK3F9! I fixed it, thanks 🙂

bschrag18:11:32
replied to a thread:Given that the form using `let` and `macroexpand` directly below macroexpands just fine, can anyone explain why the analogous definition following that form errors out during macro expansion? `exorcise.core&gt; (let [term-string "a person"]` `(macroexpand `(tm/with-matching-template-symbols ["a *type"` `,term-string]` `(declare-inferred-type *type))))` `(let*` `[*type 'person]` `(exorcise.core/declare-inferred-type exorcise.core/*type))` `exorcise.core&gt; (defn foo [term-string]` `(tm/with-matching-template-symbols ["a *type"` `term-string]` `(declare-inferred-type *type)))` `Unexpected error (NoSuchMethodException) macroexpanding tm/with-matching-template-symbols at (*cider-repl Cogex/exorcise:localhost:45999(clj)*:112:18).` `exorcise.core$foo$eval6498__6499.&lt;init&gt;()` `exorcise.core&gt;`

@l0st3d Losing the (leftover/crufty/embarrassing) eval gets past the error I'd reported. And thanks for the instructive tidying up. Now I see a different error where I try to use the macro below that calls the improved macro above. The intended binding seems inaccessible. exorcise.core> *(defmacro process-variable-term [term-string]* ``(tm/with-matching-template-symbols ["a *type"` `~term-string]` `(declare-inferred-type *type)))` #'exorcise.core/process-variable-term exorcise.core> (process-variable-term "a person") Syntax error compiling at (*cider-repl Cogex/exorcise:localhost:44009(clj)*:177:16). No such var: exorcise.core/*type exorcise.core> I'm now wondering if I've just brought too many macro expectations from Common Lisp, where this (e.g.) works: cl-user(20): *(defmacro with-bindings-list (bindings-list &body body)* ``(let ,bindings-list` `,@body))` with-bindings-list cl-user(21): *(with-bindings-list ((x 1)) x)* 1 cl-user(22): *(setf the-bindings '((x 2)))* ((x 2)) cl-user(23): *(eval (with-bindings-list ,the-bindings x))*` 2 cl-user(24): Apparently Clojure treats the final analogous form below differently. exorcise.core> *(defmacro with-bindings-list [bindings-list & body]* ``(let ~bindings-list` `~@body))` #'exorcise.core/with-bindings-list exorcise.core> *(with-bindings-list [x 1] x)* 1 exorcise.core> *(def the-bindings '[x 2])* #'exorcise.core/the-bindings exorcise.core> *(eval (with-bindings-list ~the-bindings x))*` Syntax error compiling at (*cider-repl Cogex/exorcise:localhost:44009(clj)*:169:16). No such var: exorcise.core/x exorcise.core> I've played around with different combinations of qualified vs. simple x for occurrences in (with-bindings-list [x 1] x), without success. At this point, an example might help me build confidence. Are you familiar with any Clojure macro (like my with-matching-template-symbols) that will establish a local binding for a symbol (like my term-string) passed via a parameter (like my input-form) to the macro's calling function (like my process-variable-term)?

Kazuki Yokoyama19:11:03

I'm looking for something similar to cond-> but whose predicates are functions of the cond's input like

(cond-> [] pred1 do-something1 pred2 do-something2)
where pred1 would be something like
(defn pred1 [v] (> (count v) 2))
I mean, pred1 is a function of what is being threaded (the vector, in this case). If such macro does not exist, what are the workarounds?

seancorfield19:11:13

We have condp-> (and condp->>) in our "commons" library but we have moved away from using them as they make code harder to understand more often than they improve things.

Kazuki Yokoyama20:11:16

What do you do instead? What are the suggestions? It is a pattern I see very often that I'd like to learn a more definitive solution

seancorfield20:11:50

What we found was that we almost never threaded the result into additional predicates: only the initial expression:

(condp-> expr
  some-pred some-transform)
was somewhat common but
(condp-> expr
  pred1 transform1
  pred2 transform2
  ..    ..)
was increasingly rare as the number of predicates grew. So it was usually clearer to just use actual conditionals (`if`, cond) or put expr in a let and do (cond-> x (pred x) transform)

seancorfield20:11:20

That said, I believe there is some consideration for including something like this in a future version of Clojure... https://clojure.atlassian.net/browse/CLJ-2732

Kazuki Yokoyama20:11:54

I see. So, there were some legitimate uses for condp->, right? Thank you for this very complete answer!

seancorfield20:11:03

"legimate" is subjective -- does it make the code easier to read than the alternatives? Some people might say "yes", others might say "no".

seancorfield20:11:06

That CLJ ticket is about a situation where the predicate is the same in all cases: keep threading while the value is "valid" under some specific predicate, rather than threading each result through each different predicate.

skylize21:11:29

You could compose multiple predicates into one. This example (written on phone, maybe buggy) assumes you don't want to mess with the original predicates, instead wrapping them up in a helper to only test on truthy input and pass the input forward on success.

(let [and-pred (fn [pred]
                   (fn [x] (and x (pred x) x))))]
   (when (-> x
            (and-pred pred1)
            (and-pred pred2)
            (and-pred pred3) (foo x))

bschrag23:11:57

This macro call works, ...

exorcise.core> (<<- (foobar nil nil))
foobar
...but Clojure complains about this macro I've written to use it.
exorcise.core> (defmacro declare-predicate-for-Prolog (predicate)
                 `(<<- (~'predicate nil nil)))
Syntax error macroexpanding defmacro at (*cider-repl Cogex/exorcise:localhost:44009(clj)*:674:16).
Don't know how to create ISeq from: clojure.lang.Symbol
Any insights?

bschrag23:11:03

Here's the backtrace:

2. Unhandled clojure.lang.Compiler$CompilerException
   Error compiling *cider-repl Cogex/exorcise:localhost:44009(clj)* at (674:16)
   #:clojure.error{:phase :macro-syntax-check,
                   :line 674,
                   :column 16,
                   :source
                   "*cider-repl Cogex/exorcise:localhost:44009(clj)*",
                   :symbol defmacro}
             Compiler.java: 7027  clojure.lang.Compiler/macroexpand1
             Compiler.java: 7092  clojure.lang.Compiler/macroexpand
             Compiler.java: 7178  clojure.lang.Compiler/eval
             Compiler.java: 7149  clojure.lang.Compiler/eval
                  core.clj: 3215  clojure.core/eval
                  core.clj: 3211  clojure.core/eval
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  667  clojure.core/apply
                  core.clj: 1990  clojure.core/with-bindings*
                  core.clj: 1990  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  152  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  218  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  217  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java:  833  java.lang.Thread/run

1. Caused by java.lang.IllegalArgumentException
   Don't know how to create ISeq from: clojure.lang.Symbol

                   RT.java:  557  clojure.lang.RT/seqFrom
                   RT.java:  537  clojure.lang.RT/seq
                   RT.java:  687  clojure.lang.RT/cons
                  core.clj:   29  clojure.core/cons
                  core.clj:  472  clojure.core/defmacro/add-implicit-args
                  core.clj:  481  clojure.core/defmacro/add-args
                  core.clj:  482  clojure.core/defmacro
                  core.clj:  454  clojure.core/defmacro
               RestFn.java:  146  clojure.lang.RestFn/applyTo
                  Var.java:  705  clojure.lang.Var/applyTo
             Compiler.java: 7010  clojure.lang.Compiler/macroexpand1
             Compiler.java: 7092  clojure.lang.Compiler/macroexpand
             Compiler.java: 7178  clojure.lang.Compiler/eval
             Compiler.java: 7149  clojure.lang.Compiler/eval
                  core.clj: 3215  clojure.core/eval
                  core.clj: 3211  clojure.core/eval
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  667  clojure.core/apply
                  core.clj: 1990  clojure.core/with-bindings*
                  core.clj: 1990  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  152  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  218  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  217  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java:  833  java.lang.Thread/run

dpsutton00:11:37

(predicate) needs to be [predicate]. Clojure uses vector literals for the argument list

dpsutton00:11:18

could be others. your unquoting of ~'predicate is probably not what you want if you are taking a predicate arg

bschrag00:11:23

@U11BV7MTK Re [predicate], ouch. (Probably too much CL history, not enough sleep. ;-)) But with that change the output assertion is (predicate nil nil), not (foo nil nil) as intended. Not sure what to do. <<- is a macro (that calls a macro in the Prolog interface). My predicate here (for assertion to Prolog) is foo, in case that's not obvious. Unquoting isn't getting me foo, though.

bschrag00:11:11

...fixed using function instead of macro.