noprompt / meander

Tools for transparent data transformation
MIT License
921 stars 55 forks source link

Meander doesn't match map with class value #166

Closed dgr closed 3 years ago

dgr commented 3 years ago

The following doesn't match:

(meander/find {:foo java.lang.Long} {:foo java.lang.Long} true)
;; => nil

It should return true. It seems like the issue is the use of a class name in the value position of a map.

This is with Meander Epsilon 0.0.588.

dgr commented 3 years ago

But this works:

(meander/find {:foo java.lang.Long}
  {:foo (meander/pred (partial = java.lang.Long))} true)
;; => true
jimmyhmiller commented 3 years ago

@dgr Thanks for reporting this. I'm not sure about a fix for this at the moment. But I can explain why. We compile down to case statements and clojure's case doesn't work with classes.

(case java.lang.Long
  java.lang.Long true
  false)
;; =>
false
dgr commented 3 years ago

Ah, gotcha. I'm guessing this is because Clojure requires case values to be compile-time literals and classes aren't (because of the way that class loading happens?), so case can't create the fast dispatch table that it wants to use. I ran into this trying to use Meander to implement a TRS on top of the AST produced by commonmark-java, after swizzling the Java objects into maps using bean. I can work around it using a bit more swizzling of the class name into keywords or something. But I would think that using Meander like this would be a fairly common use case.

noprompt commented 3 years ago

@dgr

(meander/find {:foo java.lang.Long} {:foo ~java.lang.Long} true)
dgr commented 3 years ago
(meander/find {:foo java.lang.Long} {:foo ~java.lang.Long} true)

Interesting. Can you explain why? I believe you, but I don't understand the reason behind it.

jimmyhmiller commented 3 years ago

I should have realized that. Meander was interpreting that as the literal symbol instead of the value and so we compiled to something that matched the literal symbol.

noprompt commented 3 years ago

Yeah. @dgr Apart from special symbols such as ., ..., ?x, !x, &, and those at the front of a list that are syntax extensions i.e. meander.epsilon/scan, all symbols are treated literally.

dgr commented 3 years ago

Ah, gotcha, so because m/find is a macro, it's comparing the class object bound to java.lang.Long in the input to the un-evaluated symbol java.lang.Long and saying those aren't equal, which they definitely aren't. So, by unquoting, it gets evaluated during (m/find ?) macro expansion time (or after m/find, as macro expansion continues recursively?). Do I have that right? I'm trying to wrap my brain around the various phases and what actually gets passed and returned from m/find. I'll probably have to spend an hour playing with similar toy examples at the REPL to understand the macrology.

But the overall upshot is that this isn't a bug or even a gray corner case; it's working as expected. I just need to change my expectations appropriately. So, I'll close this issue.