I keep wanting to write patterns with different sets of unbound logic variables in m/or
statements. Is that an anti pattern? Is there a workaround? My previous example was one of those cases, but another is similar to core.match:
(let [v [[1 2]]]
(match [v]
[[[3 1]]] :a0
[[([1 a] :as b)]] [:a1 a b]))
;=> [:a1 2 [1 2]]
Does meander expose a way to detect unbound logic variables in a pattern? If so, I could also write a macro that finds the superset of logic variables for each pattern and wraps each pattern with (m/let [v1? nil v2? nil]
so that each pattern has the same set of unbound logic variables
I'm also very interested in having disjoint sets of logical variables in a m/or clause, or at least understanding better why that's not possible
this seems to work:
(m/defsyntax mor [& patterns]
(let [vars
(into []
(comp
(map r.syntax/parse)
(map r.syntax/logic-variables)
(map #(map r.syntax/unparse %))
(map set))
patterns)
all-vars (into #{} cat vars)]
`(m/or
~@(for [[pvars pattern] (map vector vars patterns)
:let [missing (clojure.set/difference all-vars pvars)]]
(if (seq missing)
(let [bindings
(into []
cat
(for [v missing]
[v nil]))]
`(m/let ~bindings
~pattern))
pattern)))))
;; usage
(m/find
{:b 2}
(mor {:a (m/some ?a)}
{:b (m/some ?b)})
[?a ?b])
;; [nil 2]
(m/find
{:a "A"}
(mor {:a (m/some ?a)}
{:b (m/some ?b)})
[?a ?b])
;; ["A" nil]
not sure if this is a good idea or not
The reason meander doesn’t allow for disjoint unbound logic variables in an or
are for the following reasons:
1. For match
, find
, and search
I don’t have an answer to the question “what do I bind these to?” Perhaps, a special unbound
value? In other words, there is no semantic. There could be a semantic but I don’t know what it should be. In the case of rewrite
I think relaxing this restriction is fine because a semantic can be defined for using an unbound logic variable on the right hand side: it fails. In fact, this is the semantic zeta
will use where you can also use or
on the right side making this actually useful.
2. Meander borrowed a lot of ideas from existing pattern matchers. Racket’s match
is one of them and, in particular, I borrowed the or
semantics from them.
I wouldn’t say wanting different sets of unbound logic variables is an “anti pattern” but I surmise that the problem is with the data. If the model has lots of ambiguity in it, so will your code. Typically, I will try to eliminate the ambiguity prior to doing heavy transforms.
I’m up for relaxing variable restrictions use or
provided a semantics or an idea for an implementation. We can use meta on the macro forms as well to pass options.
^{::m/disjoint-variables {:unbound nil}} (m/find ,,,)
did you see the syntax I tried at https://clojurians.slack.com/archives/CFFTD7R6Z/p1614218148034200?thread_ts=1614215511.033800&cid=CFFTD7R6Z ?
But I won’t automatically default to nil
myself because I think that’s rude. 🙂
Yes, I saw that. This topic has come up before.
yea, totally makes sense for the default to disallow it
In my case, I'm using it to extract data from an API, so I can't really change the API
It's possible that I'm missing using meander and should be using something like core.match
Ah, yeah, many APIs have this great feature “optional data”. Its really cool…
You’re not misusing the library.
One of the reasons I started this project was because I did not like core.match
.
Let’s solve this problem. There’s at least 2 other people that want a solution.
more or less, I'm trying to find a way to idiomatically deal width a values like results where either you have
1. a success, something like {:type result :val {:a :some-value}}
2. a failure, something like {:type :err :msg "You're did it wrong!"}
My best shot is enabling this via meta data on the form. It is probably the easiest to achieve (for me) without spending too much time. You’re new here but I’ve really been trying to focus on zeta
, only fixing bugs, etc.
or some equivalent to https://clojure.org/guides/spec#_multi_spec
Is there something off putting about having two clauses?
(m/match M
{:type :result ,,,}
A
{:type :err ,,,}
B)
I’m guessing theres more to the story?
well, in my case, the return value is surrounded by a bunch of boilerplate that I'm also extracting values from, but it's easy enough to split it up into two matches (ie. two steps)
My use case is currently already solved well by meander, I was just wondering if there was an already existing approach that was more idiomatic
FWIW another “trick” you can apply is to grab all the other stuff as usual and process the inner stuff with m/cata
.
{:other ?stuff
:around {:in ?here}
:random-junk [(m/cata !xs) ...]
{:stuff ?stuff, :here ?here, :no-longer-junk !xs}
m/cata
is like recur
but over the whole system and how you transform nested stuff using the same set of rules.
very cool. I've been using meander to extract data from some verbose xml and it's been great.
Once I figured out how to match on unordered lists, it worked like a charm
the question about m/or
is because now I just want to use meander to improve other parts of the code
Take a look at m/cata
. I think there’s some documentation that explains it. It can be very effective at solving icky problems like these.
Also, I think would like to make people happy and solve the disjoint variable problem. If the meta thing will work, I’ll make a patch soon. If there’s a better idea, I’m open to it.
I’m glad you’re enjoying it and it is helping you. 🙂
I do need to sign off for the night but I’m down to chat more about this tomorrow; get input from others.
Thanks for your help. Have a great night!
Likewise!