Fork me on GitHub
#meander
<
2020-05-05
>
pbaille06:05:05

thank you for the exemple, it is nice, the with form is really powerful , I can honestly say that meander is the most exciting clojure lib i've seen for a long time! I hope it will continue to grow šŸ™‚ thank you a lot for the hard work.

pbaille06:05:43

Here how the https://github.com/cgrand/seqexp lib handle a similar thing (simplified). this is sweet I think:

;; match a defn-like body (including name, optional docstring, optional metadata
;; and multiple arities)
=> (def args+body (se/cat vector? (se/* se/_)))

#'playground/args+body

=> (se/exec
     (se/cat
       (se/as :name symbol?)
       (se/? (se/as :docstring string?))
       (se/? (se/as :meta map?))
       (se/|
         (se/as :body args+body)
         (se/as :bodies
           (se/+ (partial se/exec args+body)))))
     '(fn-name "some-doc" {:meta :data}
        ([a] ...)
        ([a b] ...)))

{:rest (), :match (fn-name "some-doc" {:meta :data} ([a] ...) ([a b] ...)),
 :bodies (([a] ...) ([a b] ...)),
 :name (fn-name),
 :docstring ("some-doc"),
 :meta ({:meta :data})}
does the se/? (zero or one) can be abstracted via defsyntax? I've seen that in the implementation there is a possibility to set the maximal occurence count of a repeated pattern, could we use that to do so ? do you think that something like ..0-1 for expressing such thing makes sense ? (I didn't had time to hack it but I will certainly try someday).

timothypratley21:05:22

I for one love the idea of something like ..0-1 can we brainstorm on it a bit? I think 0-1 is maybe a bit too range oriented, but maybe thatā€™s perfect???

timothypratley21:05:38

..?n-?m seems to make sense :thinking_face:

timothypratley21:05:40

And it can express: ..0-1 and ..1-?n to follow regex ? and + rulesā€¦

timothypratley21:05:35

Alternatively going straight to ..? and ..+ might be more direct, but less flexible (as you canā€™t say I want 3-5ā€¦ or support both!!! everything šŸ™‚

noprompt21:05:55

I was planning to add a more verbose operator to zeta

(m/repeated patterns ,,, :min <min-pattern> :max <max-pattern>)

noprompt21:05:29

If you did something like

(m/repeated 1 :min ?n :max ?n)
you get a greedy star
(m/repeated 1 :min 0 :max ?n)
youā€™d get an increasingly greed star

Jimmy Miller21:05:27

Ignoring syntax for a second. I think this should be achievable in epsilon. You can see the various repeat length things defined here. Should be able to add a new type. https://github.com/noprompt/meander/blob/afc410d6d82f719cf8f39c4fedf5b1cdb761fb21/src/meander/syntax/epsilon.cljc#L1172-L1276

noprompt21:05:32

@U5K8NTHEZ You mean adding m/repeated?

Jimmy Miller21:05:52

Maybe? At the very least some more flexible repeat operator, or covering some we don't have.

timothypratley21:05:09

Oh sweet, I think you are saying: (if (integer? n) ā€¦) could be: if + return min-length 1 if ? return max-length 1

timothypratley21:05:32

that seems easy and powerful šŸ™‚

timothypratley21:05:04

Ahhh dumb question from meā€¦ what are :rp* and :rp+ ? šŸ™‚

timothypratley21:05:19

it looks like :rp* is ā€¦ and :rp+ is ..3 but I got confused because :rp+ returns max length Inf (which maybe doesnā€™t matter, I just thought it would be 3)

noprompt21:05:46

Thatā€™s 3 or more. But we really need a thing allows more control.

timothypratley22:05:01

Oh well just to enumerate some optionsā€¦ ..2..5 might be swell šŸ™‚

timothypratley22:05:31

..2-5 is great but I worry that kebab variables might not make sense

timothypratley22:05:20

..?a-?b for example seems ok, but ..?some-weird-name-?some-other-name seems ambiguous

timothypratley22:05:58

where ..?some-weird-name..?some-other-name is more obvious

timothypratley22:05:14

In hindsight I wish ..3 meant exactly 3, and ..3.. meant 3 or more šŸ˜›

timothypratley22:05:13

FWIW Iā€™m pretty sure just ..? is need, because ..1 is exactly ..+ anyhow

timothypratley22:05:16

..0..1 is more precise though

noprompt22:05:31

Yeah and that syntax is actually allowed by the reader.

noprompt22:05:17

Wouldnā€™t ..3..3 be exactly three?

noprompt22:05:42

The thing I donā€™t have an answer for is in this regard is the greediness.

noprompt22:05:22

Because ..?n..?m can have many solutions depending on the situation.

noprompt22:05:55

Same goes for ..?n..?n

timothypratley22:05:59

Hmmm isnā€™t it just a mechanical translation to the min/max multimethods though?

timothypratley22:05:05

Iā€™m not sure I follow

timothypratley22:05:16

Like here:

;; 3
       (nat-int? count-pattern)
       (clj/let [?n count-pattern
                 ellipsis (clj/symbol (str ".." ?n))]
         `(and (seqable (or (and ~pattern !gather#) _gather#) ~ellipsis)
               (guard (<= ~?n (count !gather#)))))
Just needs to consider guarding the max as well?

timothypratley22:05:04

oh if they are both unbound its a different case

noprompt22:05:10

Even with min/max constraints you still need a way to specify greediness. The example would be something like

[!xs * !ys ...]
Where the * represents the greedy Kleene star. In this case !ys should never collect any values because of the greediness.

timothypratley22:05:39

Well you know my preference is greedy everything šŸ™‚

noprompt22:05:56

Yah, and I suspect thats the case for many, but not all patterns.

timothypratley22:05:00

But in the short term we could also just punt and say donā€™t support variables in both places

noprompt22:05:09

But this is way more important for subsitution.

timothypratley22:05:24

I think just supporting number (without variables) would already be a big step forward

noprompt22:05:02

On zeta the pattern

[!xs ... !ys ...]
on the RHS produces all possible ways to drain the !xs and !ys and though this is interesting and right in the eyes of searching/generation being categorical duals, its probably not what you want 99% of the time.

noprompt22:05:25

So greed becomes relevant.

noprompt22:05:46

Its not really a point of contention in traditional matches because ambiguity is excluded by design.

noprompt22:05:52

Meander is working from a different direction, especially on zeta, where the starting point is ambiguity; being able produce a full space of solutions by all possible ways to yield bindings on the LHS and generate data using those bindings on the RHS.

noprompt22:05:36

> I think just supporting number (without variables) would already be a big step forward

noprompt22:05:48

Do you just mean something like ..3..10 ?

timothypratley22:05:46

The basic form solves 2 real and recurring case in my mind: 0 or 1 optional semantics, and exactly 50 matches. And also covers everything in between.

noprompt22:05:37

Sorry for dropping the ball on this conversation, Iā€™ve been on vacation. Iā€™m open to updating the epsilon syntax to support this.

Jimmy Miller15:05:04

Yeah I personally am in favor of having an optional operator. But there are some complications because of meanders more advanced functionality.

noprompt17:05:17

The zeta version of the project will support the regex style ? operator with the caveat that logic variables must be handled.

noprompt17:05:29

You can do something like this, however,

(m/defsyntax ? [pattern tail]
  `(m/with [%tail# ~tail]
     (m/seqable & (m/or (m/seqable ~pattern & %tail#)
                        %tail#))))

(m/match '(1 3 4)
  (?x & (? 2 ?tail))
  [?x ?tail])
;; => [1 (3 4)]

(m/match '(1 2 3 4)
  (?x & (? 2 ?tail))
  [?x ?tail])
;; => [1 (3 4)]

timothypratley20:05:13

but can only appear at the end šŸ˜ž

timothypratley20:05:46

wellā€¦ I guess you can nest expressions

timothypratley20:05:15

if you make ?tail be something more complicated.

noprompt20:05:22

Iā€™ve been considering a way to make extensible infix operators like ? or & or whatever but I feel a lot of uncertainty about the implications.

noprompt20:05:45

On the zeta branch, its possible to stack & such that you can write

(?x & (? 2 ?tail) & ?other-tail)

timothypratley20:05:54

(m/match [1 ā€œthis is fineā€ :foo] (?n & (? ?s ((m/pred keyword? ?k)))) [?x ?s ?k]) Syntax error macroexpanding meander.match.epsilon/match at (scratch.clj:10:1). Every pattern of an or pattern must have references to the same unbound logic variables.

timothypratley20:05:02

looks like optional things canā€™t be variables with this techniqueā€¦ a let would be required to make it nil

timothypratley20:05:26

ie: to solve the original problem of maybe docstrings maybe meta

timothypratley20:05:56

but this doesnā€™t work either: (m/match [1 ā€œthis is fineā€ :foo] (?n & (? (m/or ?s (m/let [?s nil])) ((m/pred keyword? ?k)))) [?x ?s ?k]) Syntax error macroexpanding meander.match.epsilon/match at (scratch.clj:10:1). Every pattern of an or pattern must have references to the same unbound logic variables.

timothypratley20:05:13

bah I guess Iā€™m putting the or in the wrong spot

timothypratley20:05:56

(m/match [1 "this is fine" :foo]
  [(m/pred number? ?n) & (m/with [%tail [(m/pred keyword? ?k)]]
                           (m/or [(m/pred string? ?s) & %tail]
                                 (m/and (m/let [?s nil])
                                        %tail)))]
  [?n ?s ?k])
;; => [1 nil :foo]

timothypratley20:05:08

^^ but this canā€™t neatly be wrapped in a defsyntax

timothypratley20:05:30

The thing I like about using a memory variable is that the semantics for substitution remain clear šŸ˜›

timothypratley20:05:39

So I feel itā€™s a more symmetric approach.

timothypratley21:05:47

n = 0|1 is just a subset of many constraints

timothypratley21:05:00

like I want 1 or more (hint hint, regex +)

timothypratley21:05:44

in reality I only care about 0 or 1, and 1 or more though.