beginners

pushpankar kumar 2025-04-23T10:55:43.100499Z

Hi everyone, I am trying to build a function to check if a data structure matches a pattern

(defmacro matches? [data pattern]
  `(let [pattern#  ~pattern]
     (clojure.core.match/match [[~data]] [[pattern#]] true :else false)))

(let [data [1 1 1]
      pattern '[1 1 _]
      pattern2 '[1 1 1]]
      [(matches? data pattern) (matches? data pattern2)])
The output of this [false true] Does anyone know why? And how can I build a matches? function

p-himik 2025-04-23T11:03:10.469989Z

In pattern, the _ bit is a symbol. When used in a macro, it remains a symbol value. So it's like you had (let [underscore (symbol "_")] ...) and pattern is actually '[1 1 underscore]. Whereas clojure.core.match/match must see _ as a literal part of the pattern - not as a symbol value inside the pattern value.

pushpankar kumar 2025-04-23T11:07:36.836039Z

Is there a way to unquote the pattern before passing to core.match. I tried things like (second ~pattern) but that didn't work. During macro expansion pattern is just a symbol

p-himik 2025-04-23T11:11:55.619649Z

It's not about unquoting, it's about values. You'd have to make the macro's code aware of all the local bindings available at the time the macro is expanded. And I don't think you can do it. See this question and following responses: https://clojurians.slack.com/archives/C03S1KBA2/p1603298420425300

pushpankar kumar 2025-04-23T11:12:52.362519Z

Oh!! Thanks.. let me check

p-himik 2025-04-23T11:15:16.878639Z

Well, for this particular case you can use something like this:

(defmacro matches? [data pattern]
  (let [pattern (if (symbol? pattern)
                  (.eval (.-init (&env pattern)))
                  pattern)]
    `(clojure.core.match/match [[~data]] [[~pattern]] true :else false)))
But don't do it. It breaks everything that's not clojure.lang.Compiler, even macroexpand doesn't work with the above macro. And it won't work with anything that's not a constant, so while '[...] works, (vector ...) doesn't.

1
pushpankar kumar 2025-04-23T11:19:35.028479Z

It works but &env thing is very new to me. I think of some other way of doing things.

👍 1
p-himik 2025-04-23T11:21:37.476549Z

Since clojure.core.match/match is a macro and you seem to need run-time patterns, it's just not a suitable thing to use, at all. I don't really know what a suitable alternative would be. But it would somehow have to be able to differentiate between "symbol as a value" and "symbol as a placeholder".

p-himik 2025-04-23T11:22:21.165909Z

match is suitable if you can remove the need for run-time patterns and be content with patterns that exist during macro expansion time.

pushpankar kumar 2025-04-23T11:29:09.271109Z

I am thinking of simply doing tree traversal and comparing each element. I am building this function for dev time debugging/data exploration, so performance is not a big deal.

Ludger Solbach 2025-04-23T11:51:58.706189Z

If you can devise a grammar for your data you could use instaparse. But maybe it's a "shot with cannons on sparrows".

Ludger Solbach 2025-04-23T11:48:14.364879Z

I'm traversing a graph recursively depth first and I'm building the result in an accumulator. I want to return a vector as insertion order is relevant for the result. On the other hand, each entry should be included only once. My first idea was to have the vector for the result and a set for deduplication in the accumulator. Another option would be to deduplicate the returned vector (maybe also using a set). How would you handle this insertion ordered result?

p-himik 2025-04-23T11:52:43.440539Z

I would go with vector + set.

p-himik 2025-04-23T11:53:10.930689Z

Or an ordered set from https://github.com/frankiesardo/linked.

👀 1
Matti Uusitalo 2025-04-23T11:55:08.186379Z

How about java.util.LinkedHashSet https://docs.oracle.com/javase/8/docs/api/java/util/LinkedHashSet.html

p-himik 2025-04-23T12:03:08.069929Z

Or that, if mutable data is fine and compatibility with CLJS or other targets is not needed.

Jonas Östlund 2025-04-23T12:10:40.568989Z

I would consider accumulating to the vector using a distinct transducer. This code snippet illustrates what I mean:

user=> (let [conj-distinct ((distinct) conj)]
   (reduce conj-distinct [] [1 2 3 2 1 2 3 6 3 6 7]))
[1 2 3 6 7]
Edited to use let.

p-himik 2025-04-23T12:11:21.291489Z

You would have to lug the same transducer around, since it's stateful.

Jonas Östlund 2025-04-23T12:11:27.027399Z

Just make sure to not reuse conj-distinctoutside the scope of the algorithm since it is stateful.

Ludger Solbach 2025-04-23T12:25:06.861439Z

I would then put it at the boundary of the algorithm just before returning the result or even completely outside. Thanks for the ideas, now I have a few options to think about.

Jonas Östlund 2025-04-23T14:03:05.275339Z

You would instantiate conj-distinct every time you instantiate an empty vector into which you accumulate the result. But maybe the easiest is to just pick one of the options that others have already suggested.

phronmophobic 2025-04-23T16:22:18.373099Z

I've used https://github.com/quoll/tiara a few times with success. In particular, ordered-set may be of use.

Sciuciu 2025-04-23T15:26:54.196279Z

Hi! I have popped into this tutorial (https://ericnormand.me/guide/clojure-web-tutorial) and in it the author mentions the part 2. Does anyone know if it is already published and the URL?

ericnormand 2025-04-24T18:31:59.888819Z

No I never finished it

seancorfield 2025-04-23T15:42:29.414039Z

@ericnormand?

Sciuciu 2025-04-25T13:46:36.910449Z

Thanks @seancorfield @ericnormand for the replies and Eric for the tutorial the first part, almost finished.