meander

All things about https://github.com/noprompt/meander Need help and no one responded? Feel free to ping @U5K8NTHEZ
micha 2020-02-13T03:10:45.201900Z

is there a nicer way to have an optional subpattern, like:

(m/or {:foo ?bar :baz ?baf} (m/let [?bar nil ?baf nil]))

micha 2020-02-13T03:11:35.202500Z

like m/? in glob lingo kind of

noprompt 2020-02-13T03:40:43.207100Z

Not really but I’m working toward having a thing like this on next branch of the project.

micha 2020-02-13T03:41:36.208700Z

cool thanks!

noprompt 2020-02-13T03:45:36.212300Z

Most of the time, this is fine. Actually, I think it’s nice that there’s no convenience for this because it has a tendency to encourage models that have more consistency. That’s just me though. OTOH I think defaults for unbound logic variables is useful and has a place.

micha 2020-02-13T03:46:18.213200Z

more consistency?

noprompt 2020-02-13T03:46:56.214Z

For maps though, we don’t check if the key has a value. We use get not find.

micha 2020-02-13T03:47:26.215Z

yep

micha 2020-02-13T03:48:02.216200Z

seems like a lot of times the structure you're maching against may be optimized for size

micha 2020-02-13T03:48:19.216700Z

so there will be optional parts

noprompt 2020-02-13T03:48:50.217900Z

By consistency I mean, a data model that doesn’t have a bunch of optionality. Some optionality is fine and it’s why map patterns work they way the do. But optionality implies logic putting more branches in the code if it’s not defaulted before moving it downstream.

noprompt 2020-02-13T03:51:53.220500Z

But I want the pattern matcher to be strict by default with the option to be less restrictive if you need.

noprompt 2020-02-13T03:52:25.221400Z

And I’m working toward that second part! 😄

micha 2020-02-13T03:53:04.222500Z

i was looking in &env for bindings

noprompt 2020-02-13T03:53:18.222900Z

I can put those in there.

micha 2020-02-13T03:53:26.223200Z

to use defsyntax to implement this

noprompt 2020-02-13T03:54:29.225200Z

But what you want is to use the parser to get the variables of the pattern passed in so that you can make the let hah or you can cheat and just look for them with tree seq or something! 😉

noprompt 2020-02-13T03:54:49.225700Z

(That’s more dangerous though.)

micha 2020-02-13T03:55:00.225900Z

haha yes, that would be the easy way for sure

micha 2020-02-13T03:55:50.226700Z

i see some examples, like subst parses the form

noprompt 2020-02-13T03:55:54.226900Z

All of this stuff will get better though in time.

noprompt 2020-02-13T03:57:06.228400Z

I can help more in a bit but it sounds like you’ll figure it out. 👍

micha 2020-02-13T03:57:24.228600Z

yeah so far pretty smooth!

noprompt 2020-02-13T04:07:48.229200Z

Fair warning: there’s a ton of code in there and mostly its because I was on new ground and exploring new techniques and ideas. The code on the zeta arm is being written mostly with epsilon and compiled to Clojure that uses the new zeta runtime. The quality of the implementation is much, much better.

micha 2020-02-13T04:09:26.229400Z

sounds great

micha 2020-02-13T04:30:46.229600Z

(defsyntax opt
  [pattern]
  (let [syms (atom [])]
    (->> (r.subst.syntax/parse pattern &env)
         (walk/prewalk #(let [t (:tag %) s (:symbol %)]
                          (when (and (map? %) (#{:lvr :mvr} t))
                            (swap! syms into [s (when (= t :mvr) [])]))
                          %)))
    `(e/or ~pattern (e/let [~@(deref syms)]))))

noprompt 2020-02-13T05:15:43.229800Z

@micha There are some utilities for pulling out variables like this.

(require '[meander.match.syntax.epsilon :as m.match.syntax])
(require '[meander.syntax.epsilon :as m.syntax])

(defsyntax opt
  [pattern]
  (let [syms (atom [])]
    (if (m/match-syntax? &env)
      (let [ast (m.match.syntax/parse pattern &env)
            lvars (m.syntax/logic-variables ast)
            binding-patterns (mapcat (juxt :symbol (constantly nil)) lvars)]
        `(m/or pattern (m/let [~@binding-pattersn])))
      &form)))

noprompt 2020-02-13T05:19:03.230100Z

You really only need to ask for logic variables because memory variables are always bound to [] even if they don’t match anything. This is why something like

[(or (pred odd? !odds) (pred even? !evens) ...]
works with no complaints from the compiler. Logic variables can’t be initialized like this.

micha 2020-02-13T05:19:42.230300Z

oh sweet

markaddleman 2020-02-13T16:17:57.230500Z

fyi - The code at the top of this thread doesn't compile. I'm getting Unable to resolve symbol: ?t in this context from the guard clauses. Here's my full test case:

(m/search [{:time 1 :msg "hello"} {:time 2 :msg "goodbye"}]
            (m/and (m/scan {:time ?t :as ?m})
                   (m/separated {:time (m/and ?t1 (m/guard (<= ?t1 ?t)))}
                                #_?m
                                #_{:time (m/and ?t2 (m/guard (< ?t2 ?t)))}))
            ?m)

markaddleman 2020-02-13T16:39:20.230700Z

I was thinking about another approach: Is it safe to "exit" meander by using m/app and perform the reduction in Clojure-land? The thing that concerns me is that I believe that ends up assuming too much about Meander's runtime order of operations. In this case, I think it assumes that the m/scan is executed once and won't be revisited. In this trivial example it is but I don't know about more complex situations.

jimmy 2020-02-13T16:42:05.230900Z

That code not working definitely seems like a bug. Will see if I can find the time to track it down.

jimmy 2020-02-13T16:42:20.231100Z

I'm not 100% sure what you mean by scan only being executed once.

jimmy 2020-02-13T16:52:06.231300Z

I still consider the error you are getting a bug, but it can be fixed by moving the guards. That said, that code does not do what you want.

jimmy 2020-02-13T16:53:54.231500Z

You should be safe to use m/app for something like this. I did work on a reduction example using cata, but it is probably more than you want.

jimmy 2020-02-13T16:58:44.231700Z

I do think this is a good use case that we should make simpler.

markaddleman 2020-02-13T17:24:07.231900Z

I think part of my problem is that I don't have a good conceptual model for meander. I think of it as kind of a query system or as a simplistic constraint logic system (i know I'm wrong but I don't have a good feel for how wrong I am). Neither query and constraint systems would guarantee the order in which the scan would run or even how many times it would run. Since the result of m/app find-latest-data relies on processing the entire set of data, I got concerned.

markaddleman 2020-02-13T17:25:05.232100Z

I'll play around to see if I can get your example to compile by moving around the guards. Thanks a bunch for your help

noprompt 2020-02-13T18:25:44.232300Z

Sorry about this bug. guard kind of sucks in this regard because it depends on variables being bound and, to your point, Meander doesn’t have a declared evaluation model which results in bugs like this one. This is going to change in zeta where the model will always have the semantic left to right, top to bottom.

1👍
noprompt 2020-02-13T18:27:32.232600Z

Even still, I’m looking at this again and am realizing its the wrong approach anyway.

jimmy 2020-02-13T19:21:06.232900Z

Without seeing a bit more of what sort of transformation you are doing in context, I can't be sure. But I would suggest trying things out, seeing how they work and if you find something you are not expected, we can definitely look at it. meander is kind like a query system and kind of like a constraint system. But for the most part, you shouldn't be worried about ordering and things like that.

1👍