Fork me on GitHub
#meander
<
2021-02-25
>
phronmophobic01:02:54

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]]

phronmophobic01:02:51

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

Carlo01:02:28

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

phronmophobic01:02:48

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]

phronmophobic01:02:10

not sure if this is a good idea or not

noprompt06:02:25

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.

noprompt07:02:37

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.

noprompt07:02:41

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 ,,,)

noprompt07:02:30

But I won’t automatically default to nil myself because I think that’s rude. 🙂

noprompt07:02:50

Yes, I saw that. This topic has come up before.

phronmophobic07:02:02

yea, totally makes sense for the default to disallow it

phronmophobic07:02:46

In my case, I'm using it to extract data from an API, so I can't really change the API

phronmophobic07:02:18

It's possible that I'm missing using meander and should be using something like core.match

noprompt07:02:25

Ah, yeah, many APIs have this great feature “optional data”. Its really cool…

noprompt07:02:23

You’re not misusing the library.

😌 6
noprompt07:02:10

One of the reasons I started this project was because I did not like core.match.

😁 6
noprompt07:02:07

Let’s solve this problem. There’s at least 2 other people that want a solution.

phronmophobic07:02:06

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!"}

noprompt07:02:53

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.

noprompt07:02:53

Is there something off putting about having two clauses?

noprompt07:02:43

(m/match M
  {:type :result ,,,}
  A

  {:type :err ,,,}
  B)

noprompt07:02:07

I’m guessing theres more to the story?

phronmophobic07:02:18

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)

phronmophobic07:02:14

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

noprompt07:02:43

FWIW another “trick” you can apply is to grab all the other stuff as usual and process the inner stuff with m/cata.

🤯 3
noprompt07:02:45

{:other ?stuff
 :around {:in ?here}
 :random-junk [(m/cata !xs) ...]
{:stuff ?stuff, :here ?here, :no-longer-junk !xs}

noprompt07:02:33

m/cata is like recur but over the whole system and how you transform nested stuff using the same set of rules.

phronmophobic07:02:29

very cool. I've been using meander to extract data from some verbose xml and it's been great.

phronmophobic07:02:53

Once I figured out how to match on unordered lists, it worked like a charm

phronmophobic07:02:48

the question about m/or is because now I just want to use meander to improve other parts of the code

noprompt07:02:18

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.

noprompt07:02:56

I’m glad you’re enjoying it and it is helping you. 🙂

😁 3
noprompt07:02:17

I do need to sign off for the night but I’m down to chat more about this tomorrow; get input from others.

phronmophobic07:02:40

Thanks for your help. Have a great night!