is there a nicer way to have an optional subpattern, like:
(m/or {:foo ?bar :baz ?baf} (m/let [?bar nil ?baf nil]))
like m/?
in glob lingo kind of
Not really but I’m working toward having a thing like this on next branch of the project.
cool thanks!
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.
more consistency?
For maps though, we don’t check if the key has a value. We use get not find.
yep
seems like a lot of times the structure you're maching against may be optimized for size
so there will be optional parts
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.
But I want the pattern matcher to be strict by default with the option to be less restrictive if you need.
And I’m working toward that second part! 😄
i was looking in &env
for bindings
I can put those in there.
to use defsyntax to implement this
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! 😉
(That’s more dangerous though.)
haha yes, that would be the easy way for sure
i see some examples, like subst
parses the form
All of this stuff will get better though in time.
I can help more in a bit but it sounds like you’ll figure it out. 👍
yeah so far pretty smooth!
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.
sounds great
(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)]))))
@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)))
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.oh sweet
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)
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.
That code not working definitely seems like a bug. Will see if I can find the time to track it down.
I'm not 100% sure what you mean by scan only being executed once.
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.
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.
I do think this is a good use case that we should make simpler.
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.
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
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.
Even still, I’m looking at this again and am realizing its the wrong approach anyway.
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👍