meander

All things about https://github.com/noprompt/meander Need help and no one responded? Feel free to ping @U5K8NTHEZ
ikrimael 2020-07-24T19:32:37.181200Z

one thing I've experimented with is adding extraneous semantic terms to the (m/cata) sub expressions

ikrimael 2020-07-24T19:35:08.181700Z

so that it's' easy to demarcate which subexpression it's meant to recurse into

ikrimael 2020-07-24T19:37:28.183900Z

my general takeaway this week though is mixed; the syntax/form is concise that it doesn't feel like a chore but debugging the recursion patterns is quite frustrating

ikrimael 2020-07-24T19:38:29.184600Z

caveat: that might also be colored by basic clojure debugging knowledge gaps

ikrimael 2020-07-24T19:39:28.185400Z

don't really have a solution/anything actionable; just sharing thoughts outloud

ikrimael 2020-07-24T19:39:56.186Z

maybe if anything, next week i'll add some debugging walkthrough examples to the cookbook

jimmy 2020-07-24T19:58:48.189Z

> one thing I've experimented with is adding extraneous semantic terms to the (m/cata) sub expressions so that it's' easy to demarcate which subexpression it's meant to recurse into There is a pattern that we've used a few times to accomplish what you are talking about:

(m/rewrite [[1 2] [3 4]]
  [?x ?y]
  [(m/cata (`vec-pattern-1 ?x)) (m/cata (`vec-pattern-2 ?y))]

  (`vec-pattern-1 [?a ?b])
  {:vec1 [?a ?b]}


  (`vec-pattern-2 [?a ?b])
  {:vec2 [?a ?b]})
Here I have two completely overlapping patterns, but I can decide which one I want to cata into. In zeta, we use this pretty extensively. Here is an old commit where you can see it directly at play, we have actually abstracted it out a bit in new versions because we used it so often. https://github.com/noprompt/meander/blob/0143dadb5aced8aef4bf48054ba4b74c1a91f3ab/dev/meander/dev/match/zeta.cl In the current version we call them meta function and have a macro that generates some defsyntax patterns for us. https://github.com/noprompt/meander/blob/e54e0f2ea1b4cbd305eb803370d1ac199cb67795/dev/meander/dev/match/zeta.clj#L13-L16

👌 1
jimmy 2020-07-24T20:04:21.189400Z

I trick I do for debugging these things is to add a catch all and intentionally misspell something.

(m/rewrite [[1 2] [3 4]]
  [?x ?y]
  [(m/cata (`vec-pattern-1 ?x)) (m/cata (`vec-pattern-2 ?y))]

  (`vec-pattern-misspelled [?a ?b])
  {:vec1 [?a ?b]}

  (`vec-pattern-2 [?a ?b])
  {:vec2 [?a ?b]}
  
  ?x ?x)

;; => 

[(wander.core10/vec-pattern-1 [1 2]) {:vec2 [3 4]}]
So now I can see exactly what was going to go down the vec-pattern-1 code path. I will admit this isn't ideal but pretty handy. We have talked about how if we had an interpreter we could maybe make debugging these things easier.

noprompt 2020-07-24T20:21:17.192800Z

> but debugging the recursion patterns is quite frustrating I have felt this one too once I’ve built up a large enough system. It’s a problem with a solution and it’s pretty close to the top of the todo list.

👍 1
ikrimael 2020-07-24T20:25:06.193100Z

ah this is pretty great

ikrimael 2020-07-24T20:25:32.193300Z

i think i'll definitely walkthrough some debugging examples then in the cookbook

ikrimael 2020-07-24T20:28:52.193900Z

somewhat related, i'm having trouble determining when non-exhaustive matches trigger failures vs. not

ikrimael 2020-07-24T20:29:36.194Z

e.g. i thought i had to add (m/some ) everywhere on my map expression matches but that doesn't seem to be the case

ikrimael 2020-07-24T20:32:06.194100Z

and last night I noticed for first time that non-exhaustive matches don't trigger errors sometimes? I've been using (m/cata) & (m/rewrite) a lot more if that adds more context

noprompt 2020-07-24T20:33:04.194500Z

Only match complains about exhaustion.

noprompt 2020-07-24T20:34:04.195600Z

Normally what I do for rewrite is to make my final clause look something like

(m/rewrite _
  ,,,
  ?x
  [:MISTAKE ?x])

ikrimael 2020-07-24T20:34:08.195700Z

that would explain it!

noprompt 2020-07-24T20:35:57.197100Z

And this isn’t immutable behavior, we can support complaining about exhaustion in rewrite.

noprompt 2020-07-24T20:36:46.197800Z

I suppose to that extent we could also do so for find.

ikrimael 2020-07-24T20:37:14.198Z

oh? i was going to leave that convo thread for another day but now that you bring it up.. :)

noprompt 2020-07-24T20:38:38.199600Z

Ha! Yeah, by all means share your thoughts. I mean, if people don’t share their struggles and other people don’t run into them personally, we can’t help.

👍 1
ikrimael 2020-07-24T20:40:01.200400Z

the meta questions i had were: what are the motivating reasons for said behavior? and assuming it wasn't unintuitive "here be dragons", if it's possible to change the default behavior: - auto complain on exhaustion - force {:key val} to match without using (m/some)

ikrimael 2020-07-24T20:41:15.201500Z

my reasons: i'd rather default to "strict mode" with having to put extra effort to accomodate slop; i prefer immediate failfast => easier to debug

noprompt 2020-07-24T20:42:41.202800Z

The behavior, I think fell out of history and not really a particular design choice. I started with match, search, and substitute first and then created find, and then based rewrite on top of find and substitute.

noprompt 2020-07-24T20:42:50.203100Z

Yeah, and I’m inclined to support an option to make that possible.

noprompt 2020-07-24T20:43:41.203900Z

I’ve just gotten in the habit of jotting down a catch all “mistake” rule at the bottom of the system when I start.

➕ 1
ikrimael 2020-07-24T20:44:39.204900Z

yeah makes sense and honestly, it might be a moot point/aesthetic thing

ikrimael 2020-07-24T20:44:55.205100Z

bc that's the first thing i do as soon as i hit a bug

ikrimael 2020-07-24T20:45:21.205200Z

i think at this point all my meander matches have a catchall that throws an exception with stacktrace

ikrimael 2020-07-24T20:47:26.206600Z

what do you & @jimmy put in those clauses? (subtext: a. wonder if there's some nice tricks in there b. is it something you do all the time/common => maybe a case for makingg it a default?)

noprompt 2020-07-24T20:47:45.207200Z

So something like

^::m/flag-to-throw (m/find ,,,)

👌 1
jimmy 2020-07-24T20:50:22.209300Z

Mine typically looks like what @noprompt mentioned above. Something that stands out like [:INCOMPLETE ?x]. That way if I’m doing a recursive pattern I can see where in my recursion I am stopping.

👌 1
noprompt 2020-07-24T20:53:07.211700Z

@jimmy will probably do a better job explaining this than I will but the idea behind the rules like

(`name ?x)
{:name ?x}

[!xs ...]
[(m/cata (`name !xs)) ...]
is that you’re emulating a function call.

👌 1
noprompt 2020-07-24T20:55:50.214200Z

It’s a way to give you control.

noprompt 2020-07-24T20:56:52.214500Z

It’s also extremely intoxicating. 🙂

ikrimael 2020-07-24T20:57:34.215300Z

haha; yeah, it really is

noprompt 2020-07-24T20:57:54.216200Z

But a case and point for needing explanations when shit doesn’t work.

ikrimael 2020-07-24T20:57:58.216300Z

i have to admit, it was pretty amazing the first part of the week; unfortunately the debugging aspect of it was the only bummer but i thinkn that can be solved

noprompt 2020-07-24T20:59:38.218800Z

I think it’s really cool when people get to the point you’re at though because in terms of experience I’m more or less where you are and at this point we can just trade techniques.

ikrimael 2020-07-24T21:01:03.220700Z

yeah; it's really making very complex term rewriting or graph optimizations really tractable

ikrimael 2020-07-24T21:01:45.222Z

or rather, i've been able to quickly do simple term rewriting and constant folding passes

ikrimael 2020-07-24T21:02:35.223Z

that i normally would've relegated to a whole separate pass with work building up necessary "infrastructure"

ikrimael 2020-07-24T21:02:57.223600Z

(unrelated but btw, @jimmy since i saw you on futureofcoding, all of this is for implementing something similar to gibber or s-ol's alv demo but for C/hlsl)

👍 1
noprompt 2020-07-24T21:03:21.224100Z

This is kind of the best situation I could hope for. It confirms a point I’ve made about regular expression elsewhere which is that because the semantics of the symbols don’t change from context to context, you no longer need context to talk about a particular program.

ikrimael 2020-07-24T21:04:08.224900Z

yup. it's also really easy to read the transformation after the fact

ikrimael 2020-07-24T21:04:45.225800Z

and also made me realize how much "unnecessary" work I do that goes into just changing shape of data

noprompt 2020-07-24T21:04:55.226200Z

Slightly off topic, are you familiar with the nano pass stuff?

ikrimael 2020-07-24T21:04:57.226300Z

especially in exploratory/design phase

noprompt 2020-07-24T21:05:05.226600Z

Right?!

ikrimael 2020-07-24T21:05:12.227Z

no not at all. what's that?

noprompt 2020-07-24T21:05:59.228Z

Basically the idea is that you build a compiler out of all these little passes that do one thing after another to an AST, etc.

ikrimael 2020-07-24T21:06:26.228900Z

haha oh my god i'm going to hate cry laugh if it's what i think it is

noprompt 2020-07-24T21:06:33.229100Z

Anyway, there’s a paper about it and that’s where cata came from.

ikrimael 2020-07-24T21:06:51.229800Z

ah no shit. thanks for the link, i'll definitely read into it tonight

noprompt 2020-07-24T21:06:58.230200Z

But it’s only on the match side.

ikrimael 2020-07-24T21:07:34.231Z

i definitely which there was way more tooling/infra for creating DSLs but I couldn't find much when I started all of this

noprompt 2020-07-24T21:08:38.233Z

cata on the substitution side was a like a light year leap for rewrite. It made it possible to do a bunch of stuff that was really awkward to do before it.

noprompt 2020-07-24T21:09:39.234Z

Totally. And we have really fantastic stuff like instparse in Clojure but doing anything remotely interesting with those ASTs using stock Clojure is tedious.

☝️ 1
noprompt 2020-07-24T21:11:05.236200Z

Because there are commonly complex relationships between siblings, grandnodes, etc that are painful to code up.

ikrimael 2020-07-24T21:11:50.237500Z

imagine trying to do that in C++ 😅 <=== wrestling with templates at 3 am, I heard the "it was at this moment that he knew he fucked up" and started looking into nim + clojure the next day

noprompt 2020-07-24T21:12:15.238400Z

Maintaining a pattern, to press the Clojure vocabulary buttons, is simple and easy.

ikrimael 2020-07-24T21:12:38.239200Z

(btw, anything else you'd recommend in the line of nanopass? the only other thing i've seen in that space is the MLIR effort by llvm i'm mostly a low level graphics guy so only 3 months into knowing about compilers/prog. langs. or the space)

noprompt 2020-07-24T21:12:48.239600Z

Yeah, this is why I’m really excited about what you’re hacking on by the way.

ikrimael 2020-07-24T21:14:22.241600Z

(*ah i should say, i also learned a lot looking at nasser's mage compiler; that approach seemed really good)

⭐ 1
noprompt 2020-07-24T21:14:23.241800Z

I dunno, there’s so much stuff to recommend but normally I don’t unless it’s about a particular topic. I heard about the nano pass framework a few years ago, forgot about it, and then someone here mentioned the cata thing and brought that back to my attention.

noprompt 2020-07-24T21:14:35.242100Z

Yes!

noprompt 2020-07-24T21:15:16.242900Z

This stuff makes me wonder “why the hell aren’t we being encouraged to exploit :inline and :inline-arities?”

ikrimael 2020-07-24T23:12:12.245500Z

@noprompt i'm watching the nanopass talks and it's fantastic ; if nothing else, highlighting some interesting ways could architect the dsl compiler I'm missing the link to meander though?

noprompt 2020-07-24T23:14:16.246100Z

The link was to the dissertation: http://www.andykeep.com/pubs/dissertation.pdf

noprompt 2020-07-24T23:14:50.246700Z

It’s mentioned on pages 4 - 5.

noprompt 2020-07-24T23:17:37.248500Z

> The match form also supports catamorphisms [68] to recur through the sub-expressions of the input forms. A catamorphism, for our purposes, recurs through sub-forms in the language until a terminal case, such as x or (quote d), is found. The simplify pattern can be rewritten to use catamorphisms as follows:

(define simplify
  (lambda (x)
    (match x
      [,x (guard (symbol? x)) x]
      [(quote ,d) `(quote ,d)]
      [(if ,[e0] ,[e1]) `(if ,e0 ,e1 (void))]
      [(if ,[e0] ,[e1] ,[e2]) `(if ,e0 ,e1 ,e2)]
      [(begin ,[e*] ... ,[e]) (make-begin e* e)]
      [(lambda (,x* ...) ,[body*] ... ,[body])
       `(lambda (,x* ...) ,(make-begin body* body))]
      [(letrec ([,x* ,[e*]] ...) ,[body*] ... ,[body])
       `(letrec ([,x* ,e*] ...) ,(make-begin body* body))]
      [(,[e] ,[e*] ...) `(,e ,e* ...))])))
> Here, the square brackets (`[ ]`) in the syntax ,[e0] indicate that a catamorphism should be applied.

ikrimael 2020-07-24T23:18:31.248900Z

aah gotcha

noprompt 2020-07-24T23:18:51.249400Z

I really would love to have something as lightweight as [ ] instead of cata.

ikrimael 2020-07-24T23:20:16.250700Z

this is really exciting though; i've been fumbling around trying to architect the compiler and it's various passes in an extensible way

ikrimael 2020-07-24T23:20:43.251300Z

and at least from what i've seen so far, this approach is very mappable to how i'm trying to use meander

ikrimael 2020-07-24T23:23:43.253700Z

*rather, the entire compiler chain can be done this way vs. i'm just using it for front end/medium end

noprompt 2020-07-24T23:27:44.253900Z

It’s certainly a sane way to do it.