meander

All things about https://github.com/noprompt/meander Need help and no one responded? Feel free to ping @U5K8NTHEZ
yuhan 2020-08-25T05:29:02.017700Z

I haven't used Meander for a while now (deemed too experimental for work use), but wouldn't the "unspecified keys" proposal conflict with the use of ?logic vars as lookup keys in a map?

noprompt 2020-08-25T15:47:21.020400Z

Yes.

yuhan 2020-08-25T05:38:59.018100Z

(let [db {:user-db {"Alice" {:id 123}
                    "Bob"   {:id 124}}
          :info-db {122 {:favorite-food "nachos"}
                    123 {:favorite-food "noodles"}
                    124 {:favorite-food "nutella"}}}]
  (m/match db
    ;; query: what is Alice's favorite food?
    {:user-db {"Alice" {:id ?id}}
     :info-db {?id {:favorite-food ?food}}}
    ?food))
;; => "noodles"

yuhan 2020-08-25T05:49:30.019700Z

can't construct a version with !memory vars at the moment, but it sounds bad for the semantics to be different depending of the type of var

šŸ‘ 1
noprompt 2020-08-25T15:40:35.020300Z

> deemed too experimental for work use Ha! šŸ™‚

noprompt 2020-08-25T15:49:35.022200Z

I see ticket #130 more or less as an invitation to think about how we can improve the visual parts of map/set patterns for the reasons mentioned in that ticket.

noprompt 2020-08-25T15:56:44.025700Z

One thing we could do is provide a way to hook into the parser via protocols.

noprompt 2020-08-25T15:58:57.028300Z

Clojure doesnā€™t respect the namespace aliases of tagged literals though which makes doing something like

#my.custom.MapPattern {!k !v}
really gnarly.

noprompt 2020-08-25T16:00:17.030100Z

The other possibility is making syntax extensions a bit more interesting.

noprompt 2020-08-25T16:00:53.030800Z

Thereā€™s also the road of flags.

timothypratley 2020-08-25T16:09:27.036800Z

FWIW Iā€™d happily accept the different behavior based on type of var to regain the structure of my expressions šŸ™‚ I have tried to think of ways to explicitly differentiate and unfortunately they all bloat the structure a lot. I really do think it boils down to treating memory variables differently in maps and setsā€¦ and that there is significant value in doing so.

noprompt 2020-08-25T16:18:41.041200Z

I think there is significant value in achieving what youā€™re after in #130, however, the culprit is the search space the container represents not the variable.

noprompt 2020-08-25T16:21:35.043600Z

I think it has been mentioned before but would you agree with

{& ([!k !v] ...)}
on the match side as a compromise for the moment pending further investigation?

timothypratley 2020-08-25T16:30:55.046100Z

Iā€™d rather just stick with map-of or {& (m/sequable than make that change!!! šŸ˜› FWIW I think that violates the structure rules

timothypratley 2020-08-25T16:31:17.046600Z

Can you explain more what ā€œthe culprit is the search space the container represents not the variableā€ means?

timothypratley 2020-08-25T16:37:08.047800Z

Maybe an example šŸ™‚

timothypratley 2020-08-25T16:41:26.051400Z

I think you meanā€¦ what is does this mean? {:a 1, !k !v} To which I say it should logically match {:a 1, :c 3} and that !k should be bound as [:a :c] order is arbitrary but matches !v [1 3]

timothypratley 2020-08-25T16:42:17.052400Z

Notably if you wanted to you can also write {:a 1, & {!k !v}} and instead have !k/!v only match the remaining map conveniently

noprompt 2020-08-25T16:54:07.059900Z

{p1 p2}
represents a submap of the match target of 1 entry. The entry has a key that matches p1 , and value that matches p2. The search space is all of the values which can be yield from the target which have the potential to match. So if the target were
{:a 1 :b 2}
the search space would be
([:a 1] [:b 2])
where one element in the space matches :a and 1 against p1 and p2 respectively, and :b and 2 against p1 and p2 respectively.

noprompt 2020-08-25T16:55:40.061100Z

The search space for a map is actually a bit more complex than this but Iā€™m choosing the simplest case for illustration.

noprompt 2020-08-25T16:59:43.064Z

Why is this important? Well if you want the variables to bind differently, you need to change the search space or change the pattern.

noprompt 2020-08-25T17:05:48.069400Z

The syntax extensions exist to change patterns which can affect how search spaces are yield.

noprompt 2020-08-25T17:06:03.069700Z

However, visually they are not satisfactory.

noprompt 2020-08-25T17:07:33.071500Z

We donā€™t have complete control over how to view a structure in a given context beyond wrapping it in a (symbol p) kind of pattern where symbol has been defined as a syntax extension.

noprompt 2020-08-25T17:09:07.073600Z

What would be nice is to be able to say something like

(notation {!k !v} (m/map-of !k !v))

noprompt 2020-08-25T17:10:46.076200Z

So instead of making the interpretation of an arbitrary pattern context sensitive with respect to the container it happens to appear in you say instead that here we say that a pattern which looks like this should be replace with a pattern that looks like this.

noprompt 2020-08-25T17:13:27.078600Z

So, for this example, you could write

{:a 1 & {!keys !vals}}
and have it expand to
{:a 1 & (map/of !keys !vals)}

yuhan 2020-08-25T17:19:18.082100Z

@noprompt that sounds like a pretty dangerous proposal..! I wouldn't trust myself with such large amount of control over the semantics of an expression

šŸ‘ 1
yuhan 2020-08-25T17:21:27.085Z

currently I can look up where each Meander operator is defined and what it does, with arbitrary notation transforms I won't even be able to tell at a glance what's being transformed and where it's defined

šŸ‘ 1
timothypratley 2020-08-25T17:22:01.085400Z

Iā€™m not really able to follow the ā€œsearch spaceā€ argument; maybe you can simplify for me ā€¦ observing that maps are sets of key-values ā€¦ lets just talk about sets, and not use any pre-existing variables like ?k or !k: letā€™s just talk about #{-k} a magical new thing that will allow me to match a set and substitute its values, is this impossible for some reason? Iā€™m explicitly not including find in this description.

yuhan 2020-08-25T17:29:48.086100Z

so this -k is bound to all the values in the set?

timothypratley 2020-08-25T17:30:36.086700Z

I know what you are going to sayā€¦ oh but -k could have matched any element! Greed is evil! To which I reply this is why I need greed.

timothypratley 2020-08-25T17:35:26.086800Z

Iā€™m trying to be abstract and just specify that Iā€™d like

(m/rewrite #{1 2 3}
  #{-k}
  #{-k})
;;=> #{1 2 3}
as a user feature šŸ™‚ I do think that means -k gets bound to all values in the set, which implies why not just do:
(m/rewrite #{1 2 3}
  (m/and #{} ?xs)
  ?xs)
So I need a slightly more motivating example šŸ™‚

timothypratley 2020-08-25T17:42:55.087600Z

(m/rewrite #{1 2 3}
  #{-k}
  #{(m/app inc -k)})
;;=> #{2 3 4}

noprompt 2020-08-25T17:44:59.087900Z

Greed is certainly not evil.

noprompt 2020-08-25T17:46:45.089600Z

What is evil is a that a pattern could have different semantics in different contexts: precisely your point about {& ([!k !v] ā€¦)} .

timothypratley 2020-08-25T17:48:55.091700Z

can you give an example of how #{-k} would have different meanings?

noprompt 2020-08-25T17:49:05.091900Z

No because it only has one meaning.

timothypratley 2020-08-25T17:49:09.092200Z

lol

noprompt 2020-08-25T17:49:11.092400Z

šŸ™‚

timothypratley 2020-08-25T17:49:35.093200Z

Iā€™m so confused šŸ˜• sorry if Iā€™m dense

noprompt 2020-08-25T17:49:46.093500Z

The search space yielded by the container determines what values itā€™s contents will be matched against.

noprompt 2020-08-25T17:49:55.093800Z

Tim, you are not dense. šŸ™‚

noprompt 2020-08-25T17:50:33.094600Z

Iā€™ve known you for, what, something like 6 years or something? Dense isnā€™t even on the map. ā¤ļø

timothypratley 2020-08-25T17:51:00.095300Z

Is it that #{?k} and #{-k} have different meanings and thatā€™s bad?

noprompt 2020-08-25T17:51:09.095500Z

And, fwiw, these conversations are really important.

noprompt 2020-08-25T17:53:39.097100Z

It is that, in general, for all patterns p the semantics of #{p} do not change.

timothypratley 2020-08-25T17:54:19.097300Z

gotcha

noprompt 2020-08-25T17:54:54.098300Z

This is why I was lamenting the fact that tagged literals could be so bulky.

timothypratley 2020-08-25T17:55:22.099300Z

^-k #{} <-- is this ok?

noprompt 2020-08-25T17:57:41.101600Z

But I wouldnā€™t want to get in the way of something like #mine #{!k} where perhaps #mine maps to

(defn mine [value]
  (reify
    m.protocols/IExpandSyntax
    (-expand-syntax [this]
      ,,,)))

timothypratley 2020-08-25T18:03:48.103500Z

is here a more direct solution here where I write my own custom tag that translates {!k !v} to (map-of !k !v) and stop complaining?

noprompt 2020-08-25T18:10:53.109300Z

That would be the notation idea. Iā€™ve been on the fence about it because, like @qythium pointed out, itā€™s crazy powerful and could be bad. That being said @jimmy has somewhat convinced me that isnā€™t necessarily a good reason to dismiss the idea on the grounds that we canā€™t stop people from obfuscation. defsyntax can easily obfuscate. Going the route of notation should involve a commitment to tooling that enables makes it easier to expand patterns as you would a macro.

yuhan 2020-08-25T18:15:06.112300Z

yeah, the last time I played around with defsyntax I found myself really wishing for a way to macroexpand sub-patterns

yuhan 2020-08-25T18:15:17.112600Z

not sure if that's already possible or meaningful

noprompt 2020-08-25T18:17:38.114Z

It is meaningful.

yuhan 2020-08-25T18:18:17.114400Z

that's a relief to hear!

yuhan 2020-08-25T18:19:18.115700Z

One really nice property I like about Meander is that all the special vars and things are drop-in replacements for literal matches

yuhan 2020-08-25T18:20:43.117200Z

there's probably a better way of putting this but it makes a lot of sense in a easy refactoring / referential transparency mindset

yuhan 2020-08-25T18:22:06.119Z

so having !k !v be context dependent would complect the whole situation IMO

noprompt 2020-08-25T18:23:23.120Z

Currently, Iā€™m a huge bottle neck with respect to the project. Iā€™ve got work related requirements and my kids at home 24/7 (two of which are being home schooled now). What I really want to figure out is how to get others involved in a maximally collaborative but minimally pressured way.

noprompt 2020-08-25T18:26:19.122200Z

I have a model for zeta that I think is mostly sound in terms of the primitives and assumptions. The interpreted model has explainability built in like spec and it works both for matching and substitution. But I need others to think about it too.

noprompt 2020-08-25T18:27:17.123100Z

Compilation can be built on top of that model which can fall back to run time interpretation if needed.

noprompt 2020-08-25T18:29:54.123700Z

(let [?x (logic-cell)
      x-&gt; (lifo-cell)
      x&lt;- (fifo-cell)
      pattern (pair (&amp;&amp; (in [1 2 3]) x&lt;- x-&gt;)
                    (&amp;&amp; (in [4 5 6]) x&lt;- x-&gt;))
      extract-bindings (fn [result]
                         (let [bindings (get result :bindings-out)]
                           {'x-&gt; (get bindings x-&gt;)
                            'x&lt;- (get bindings x&lt;-)}))]
  [;; Query the pair [2 6] against the pattern and pull out the
   ;; passing bindings.
   (map extract-bindings
        (filter pass? (query pattern [2 6] {})))
   ;; Attempt to generate 20 solutions and pull out the passing value
   ;; and binding.s
   (map (juxt :value extract-bindings)
        (filter pass? (take 30 (yield pattern {}))))])
;; =&gt;
#_
[({x-&gt; (6 2), x&lt;- [2 6]})
 ([[1 4] {x-&gt; (4 1), x&lt;- [1 4]}]
  [[1 5] {x-&gt; (5 1), x&lt;- [1 5]}]
  [[1 6] {x-&gt; (6 1), x&lt;- [1 6]}]
  [[2 4] {x-&gt; (4 2), x&lt;- [2 4]}]
  [[2 5] {x-&gt; (5 2), x&lt;- [2 5]}]
  [[2 6] {x-&gt; (6 2), x&lt;- [2 6]}])]

noprompt 2020-08-25T18:40:07.128500Z

ā€œTwoā€ is what I call the underlying framework. It thinks of variables similar to how Clojure thinks of them in that names point to things but the things are cells which specify how their content is accumulated (fold) and it is dispersed (unfolded).

noprompt 2020-08-25T18:42:13.130400Z

At a more general level, patterns of which variables are subset, are objects which define what it means to be queried and also what it means for them to yield their instances.

noprompt 2020-08-25T18:43:04.130900Z

IOW, patterns represent elements of sets.

noprompt 2020-08-25T18:45:56.133Z

query asks if a value is a member of those sets and produces bindings. yield asks if a value can be produced given bindings and if so produce those values.

noprompt 2020-08-25T18:48:04.133900Z

This is deliberately not unification.

timothypratley 2020-08-25T18:48:32.134400Z

@qythium Ā could you expand on what ā€œall the special vars and things are drop-in replacements for literal matchesā€ means?

yuhan 2020-08-25T18:58:36.134700Z

I think of the "base case" for patterns as matching on a pattern made entirely of literals

(m/match [1 2 [3]] [1 2 [3]] :ok)
Then you can iteratively replace subexpressions of the pattern with more complicated concepts like logic vars
(m/match [1 2 [3]] [1 2 ?x] ?x)
knowing that the spot where the ?x goes "resolves" to what could be a base pattern

timothypratley 2020-08-25T19:01:03.134900Z

:thumbsup:

yuhan 2020-08-25T19:01:38.135100Z

I don't know if that's always true eg. cata seems to be context dependent, but I find it a useful mental model

yuhan 2020-08-25T19:13:38.136400Z

@noprompt That was quite a bit to digest but really interesting! So fifo-cells are Meander's memory variables?

noprompt 2020-08-25T19:14:11.136500Z

cata isnā€™t context sensitive in the sense that its semantics are the same wherever it appears. It is context sensitive in the sense of what the target value is recursively matched against in terms of the system it appears within.

noprompt 2020-08-25T19:23:31.141Z

Yep. I didnā€™t chuck in the use of ?x there but it works as youā€™d expect.

noprompt 2020-08-25T19:24:55.143200Z

Keep in mind this is a model for interpretation. This means, at a minimum, we have a non-macro version of search/yield.

noprompt 2020-08-25T19:25:55.144600Z

There is some other interesting stuff going on in there too if youā€™ll notice the use of SplittableRandom and seed.

noprompt 2020-08-25T19:26:53.146Z

The idea here is not only to allow for concepts of matching elements from infinite sets but also producing them.

noprompt 2020-08-25T19:29:04.149500Z

This is interesting for something like (sum ?x 3) where the query means find all ?x + 3 = TARGET and yield means all values that give you a value which can match ?x + 3 and, if you want, the bindings that makes that true.

noprompt 2020-08-25T19:29:54.150200Z

The streams of data produced by the way include failures unlike previous versions.

noprompt 2020-08-25T19:31:27.151800Z

This means that itā€™s possible to have a model that allows one to explain failures in either case, and, importantly, prevent divergence.

noprompt 2020-08-25T19:35:28.153100Z

Thereā€™s also a rough sketch of group-by-cell in there.

yuhan 2020-08-25T19:36:16.153800Z

This sounds a lot like unification, from the vague familiarity I have with both areas

yuhan 2020-08-25T19:39:01.155600Z

ah scratch that, my thinking is too muddled for that to be a coherent question

yuhan 2020-08-25T19:40:41.156800Z

I'd love to dive into this in my spare time too and contribute! It's just a little daunting how high-level the concepts seem to be from scanning through the Meander codebase

yuhan 2020-08-25T19:42:31.158200Z

Also just throwing out a random thought I had before - seeing as how Meander's vars use different sigils that are compiled into some underlying representation, could this be made into an extendable notation?

noprompt 2020-08-25T19:42:41.158600Z

I need write down what I wrote down here on that file, eh? šŸ™‚

šŸ’Æ 1
noprompt 2020-08-25T19:43:15.159500Z

I think the sigils are valuable for the macro version for at least things like logic variables.

yuhan 2020-08-25T19:43:18.159600Z

(defsigil Ā© 
  "docstring" 
  [sym &lt;other necessary args&gt;] 
  &lt;implementation, compiler/interpreter hooks etc.&gt;) 
where the sigil symbols have to come from the corresponding Unicode punctuation/symbol blocks like Haskell operators

yuhan 2020-08-25T19:43:43.160400Z

then you can go around using Ā©x in patterns with your own user defined semantics

noprompt 2020-08-25T19:43:54.160700Z

Yeah. This is something Iā€™ve been thinking about also.

noprompt 2020-08-25T19:45:40.162300Z

I think the smallest thing would be declaring variables somehow

(m/declare [$x ([m/unbound 0] 0 _ m/unbound)]
  ,,,)
you get the idea.

noprompt 2020-08-25T19:46:26.163200Z

Or maybe [$x ~rewrite-rules] to keep plain functions in the mix.

noprompt 2020-08-25T19:47:17.163500Z

Damn you covid-19!

noprompt 2020-08-25T19:48:32.164400Z

Oh, before I forget, I should plug #asami as a project to keep an eye on. šŸ™‚

šŸ‘€ 1
noprompt 2020-08-25T19:49:03.165Z

I am on the same team as the original author and am gradually becoming a contributor.

timothypratley 2020-08-25T20:05:50.167Z

Iā€™m really looking forward to the logic-cell stuff as it sounds like a general solution for aggregation/reduce!

timothypratley 2020-08-25T20:15:01.172700Z

Iā€™ve attached the ā€œmacro solutionā€ of replacing {!k !v} with {&amp; (map-of !k !v)} to issue #130 based on the suggestions here mainly to record itā€¦ I can go ahead and start using the macro in my projects and learn the hard way where it will bite me later.

šŸ˜‚ 1
timothypratley 2020-08-25T20:29:06.173Z

actually I think this is already broken: #{^&amp; ?rest}

noprompt 2020-08-25T20:30:23.173200Z

It is?

timothypratley 2020-08-25T20:30:56.173400Z

Iā€™m not sure notation is needed at allā€¦ as a user I can write a macro that translates my patterns

timothypratley 2020-08-25T20:31:36.173600Z

I mean maybe it solves some other problem šŸ™‚

timothypratley 2020-08-25T20:31:38.173800Z

and thatā€™s great.

jimmy 2020-08-25T20:32:49.174Z

We could also expose an ast transformation level if we really wanted to. Not saying we should, but it would solve the problem and also let us experiment with optimizations in an extendable way.

noprompt 2020-08-25T20:33:51.174300Z

(let [s #{1 2 3}]
  (m/match s
    #{^&amp; ?rest}
    ?rest))
;; =&gt; #{1 3 2}

(let [s #{1 2 3}]
  (m/find s
    #{^&amp; ?rest}
    ?rest))
;; =&gt; #{1 3 2}

(let [s #{1 2 3}]
  (m/search s
    #{^&amp; ?rest}
    ?rest))
;; =&gt; (#{1 3 2})

(let [s #{1 2 3}]
  (m/rewrite s
    #{^&amp; ?rest}
    #{4 5 6 ^&amp; ?rest}))
;; =&gt; #{1 4 6 3 2 5}