re
(let [data (map hash-map
(repeat :regression-test-success?)
(cycle [true false]))
regression-suite (take 10 data)]
(m/find regression-suite
(m/gather {:regression-test-success? true
:as !success-test-records}
?count)
[!success-test-records ?count]))
How can I extend this example to m/search
? I'd love to compute the total successes and failures in the same meander program.If all you want is the number of success and failures you don't need to use search. You can do this.
(let [data (map hash-map
(repeat :regression-test-success?)
(cycle [true false]))
regression-suite (take 10 data)]
(m/match regression-suite
(m/seqable (m/or {:regression-test-success? true
:as !success-test-records}
{:regression-test-success? false
:as !fail-test-records}) ...)
{:fails (count !fail-test-records)
:successes (count !success-test-records)}))
Thanks. I'm having trouble understanding the difference between m/sequable and m/scan
(seqable <pattern> ...)
is like [<pattern> …] but works for anything in clojure that is seqable. That means that everything in the collection must match that pattern.
(scan <pattern>)
actually expands to something like [_ ... <pattern> . _ ...]
. Scan is looking for anywhere in the collection that the pattern could exist. Scan actually allows you to try and match on multiple patterns. It will search through the collection finding all the elements that can match your pattern and ignore all the rest. Scan must be used with search
Finally (gather <pattern>)
says find me all the things in the collection that match this pattern and gather them up for me. Anything that doesn’t match I don’t care about it.
I should think about writing an article explaining these. None of them are actually primitive. They are things we have found useful and added to meander, but they are all just extensions built on top of the base.
This is helpful. Thanks. I noticed an Github issue to write a top-down tutorial. I think that would help me a lot. It's been a long time since term rewriting systems in school and I'm finding it hard to see the forest for the trees.
Hmm, I frequently want a non-backtracking or in m/search
. e.g. (m/x-or (m/pred number? ?x) (let [?x 0]))
instead of (m/or (m/pred number? ?x) (m/pred (complement number?) (m/let [?x 0])))
.
I shall try defsyntax. But what would this be called? Is there a precedent for the name?
Or maybe a cut operator?
(m/match [42 76] [?a . (m/app #(+ ?a %) !b) ...] [?a !b]) ;=> [42 [118]]
, but (m/match [42 76] [?a . (m/or (m/app #(+ ?a %) !b) _) ...] [?a !b]) ;=> Unable to resolve symbol ?a
.
@eraserhd I’m thinking about #3 a bit now. What do you think of this sketch?
(m/compute ?result + ?a ?b)
;; `?a` and `?b` must be bound
;; `?result` matches the result of the function application.
This is venturing near constraint territory.
(m/compute true < ?a ?b)
hmm.
This is pretty close to m/let.
m/let presumably has the same problems as m/app wrt captured variables?
I think m/compute is slightly awkward.
I wouldn't bat an eye if that's what I got.
(m/compute ?result (+ ?a ?b))
feels slighty better. In this example, only a list with simple subexpressions is allowed.
There's a form like this in Datomic. It's not clearly defined what is allowed in subexpressions in Datomic, and occasionally I can get away with something useful, but not much.
In Datomic, [(+ ?a ?b) ?result]
.
IIRC, [(+ ?a (+ 1 2)) ?result]
works, but [(+ ?a (+ 1 ?b)) ?result]
does not.
(variables have to be at the top)
And, I guess this is just let with a rule that any variables are at the top.
I think something like
(m/compute result-pattern fn-pattern arg-patterns ...)
might be have some useful properties because fn-pattern
can be bound to a fn from Clojure explicitly
(m/compute ?res ~+ ?a ?b)
Its not clear to me what extra round braces provide.
To be clear, I’m thinking of what the primitive form of this would be.
ah
Because I think m/app
would emerge from this easily.
(m/app inc ?res)
;; ==
(m/compute ?res ~inc)
Must all args, aside from f, be logic or memory variables?
But also, we could get constraints out of this too.
No.
(m/compute 2 ~+ 1 1) ;; matches
(m/compute 3 ~+ 1 1) ;; fails
Ok, then I like this.
And if you would like round braces, of course, you could syntax that on later.
right
1. wacky idea - it's possible to attach metadata to things such that + could be reversible - e.g. ?result bound, but an arg not. Although, I guess that's also not a primitive?
2. does "constraints" imply unification?
Maayybe.
hrmm. I can't think of a use for it. But I shall pay more attention.
There have been a few cases of people talking about patterns like
(m/and ,,, (m/guard (< 1 ?y)))
Regarding unification, I’ve been wanting to venture more into that territory because there’s a lot of power there and whole bunch of stuff falls out. Pattern matching is a special case of unification where one of the terms being unified is ground.
Back to 1. no that isn’t wacky but it would probably mean that we’d need to talk about domains.
Which, honestly, that’s going to have to happen anyway.
Because substitution currently does not have a failure semantic and it only computes a single result. We have an open ticket for generating values, this is essentially the search
version of subst
. A primary reason it hasn’t be started is that in order to do so a notion of failure is required.
hmm
that's kind of neat
I think so too. 🙂 Shapes are kind of trippy.
this reminds me of a completely different use case I have - testing. The problem here is that it needs much better error messages.
Totally!
btw, I read syntax.clj and some of ir.clj. Which, historically, predicts I'll contribute in a few weeks.
cljc :D
Its a bit of a mess in there. I’m sorry. I tried to keep it tidy while also trying to push out code for Clojurist Together.
I’ve started some work on the zeta
branch; the pace there will be much slower.
But epsilon
is going to be around for a while longer and it’d probably be a good idea to make an issue or two for some of the design problems therein.
app
keeps the functions as opaque so when we generate things for or
it doesn't know there is a variable in there.
{:cols
[{:tag :wth,
:bindings [],
:body
{:tag :vec,
:prt
{:tag :prt,
:left {:tag :cat, :elements [{:tag :lvr, :symbol ?a}]},
:right
{:tag :prt,
:left
{:tag :rp*,
:cat
{:tag :cat,
:elements
[{:tag :meander.match.syntax.epsilon/apply,
:function #(+ ?a %),
:argument {:tag :mvr, :symbol !b},
:meander.syntax.epsilon/original-form
(m/app (+ ?a %) !b)}]}},
:right {:tag :cat, :elements []}}},
:as nil}}],
:rhs {:value [?a !b], :op :return},
:env #{},
:refs {},
:ref-specs {}}
Definitely a bug, but not sure what the right resolution would be. Maybe we can parse the function? Maybe we should reject those things? Honestly don't know.I'm guessing you need those search semantics for other things? If not, I find match to work fairly well for things like that.
You can’t rely on the logic variable being bound at that point and that’s due to how the pattern matching compiler operates. It prioritizes parts of the match over others; it’s part of the algorithm, not a bug.
I mean it's a bug that we don't catch it and instead generate bad code.
Since we don’t do analysis of the code we should probably call this out in the documentation somewhere.
I wondered if cut would come up some day. 😄
There’s this now if you want an short overview of how to make extensions. https://github.com/noprompt/meander/blob/epsilon/doc/defsyntax.md