Making @rgchris Comfortable With New /PATH Forms

Hopefully you're warming up a bit to the generalized paths and tuples, and how they can be COMPOSE'd with pieces spliced, etc. I mentioned there was a long way to go on mechanics, but progress has proceeded one hard-won fight at a time...

Example: Tonight while firming up the idea that the SPACE rune matches literally in PARSE for arrays, I found it instantly applicable to a significant step ahead in processing refinement-y things...since space is what's used for vacant spots:

>> refinement-rule: [subparse path! [_ word!]]

>> parse [/foo] [refinement-rule]
== foo

SUBPARSE is basically like "INTO AHEAD", so it parses into the rule product of its first argument. (The first rule doesn't have to be a pattern on the input series, it could be a GROUP! that synthesizes any arbitrary series or sequence.)

So recall that:

>> as block! /foo
== [_ foo]

Also, that BLOCK! rules evaluate to the last matched thing.

1 Like

I shared your questioning -- @rgchris -- of exactly how the new path forms might be worked with easily.

The concept of being able to see leading slash paths as being "special" isn't (in my view) a good fit for things like TO conversion:

>> to path! 'a
** PANIC: Did you mean /a or a/ ?

With things like that being in limbo, code that used to be easy to write became circuitious.

DECORATE + REDECORATE May Have Cracked This

I started thinking of there being a special area of concerns regarding "stuff glued on the front of a token"--if that is how you experience it (regardless of mechanics behind it).

We have a fair number of these things now. And we also have tricks for letting them stand alone.

So I started looking at it that way:

https://rebol.metaeducation.com/t/dialecting-power-decoration-of-decorate/2579

>> map-each item [@word :tu.p.le] [redecorate '/ item]
== [/word /tu.p.le]

I Started With Constraint Functions (Not Great)

To just get things off the ground, I had to make type constraint functions to do type checking... since things like /FOO and BAR: aren't fundamental types. You end up with things like this:

something: func [arg [run-word? set-word?]] [
    ...
]

While SET-WORD? is reasonably understood, that's a bit opaque to know that /FOO is a run-word, and you end up with problems having to name every type.

And it's not ideal using constraint functions in places like PARSE:

>> parse [1 /a 2 /b 3 /c] [some [integer! | run-word?/]]
== /c

Pattern-Matching Is More Solid and Semiotic

something: func [arg [/word! word!:]] [
    ...
]

When you're in a typespec, you match the decorations on the instance of the values of the type.

This, I like.

:+1:

I'm starting to feel comfortable calling .foo a dot-word, :foo a colon-word, and /foo a slash-word when I say them out loud, but if they are types mentioned in source they are .word! and :word! and /word!.

What I do not so much like is that in contexts where you're not in a type spec block, it's more confusing and requires some kind of keyword or boilerplate to put you in a typespec-like mode.

e.g. the MATCH combinator takes a type spec block literally (does not act like a rule block, doesn't call the BLOCK! combinator). It works like:

>> parse [1 /a 2 /b 3 /c] [some [integer! | match [/word!]]]
== /c

It's actually the case that since MATCH takes its argument literally, it could assume anything that wasn't a BLOCK! was meant to be a datatype:

>> parse [1 /a 2 /b 3 /c] [some [integer! | match /word!]]
== /c

Mechanically that works, but it does get a bit weird when you are using other sequence forms:

>> parse [x: /a y: /b z: /c] [some [match word!: | match /word!]]
== /c

That's a little ugly, but remember that MATCH takes a spec block that's inherently a set of alternates. So it gets a little nicer for this case:

>> parse [x: /a y: /b z: /c] [some match [word!: /word!]]
== /c

And Of Course, You Can Name What You Want

One way of making peace with some of issues with decorated things being awkward to handle might be to say you find ways to quarantine their weirdness through naming things yourself.

I don't predict I'll be calling :FOO "refinement" (nor "get word"), so if you want to call /FOO refinement for some reason you could do that... but not as a datatype, as a match rule:

>> refinement: [match /word!]  ; or SLASH-WORD or whatever

>> parse [1 /a 2 /b 3 /c] [some [integer! | refinement]]
== /c

So that makes it powerful... if your situation is more than just a one-off, then name your pattern. And you get to be the one to name it.

P.S. You'd Probably Hate The Quasi-FENCE! Idea

https://rebol.metaeducation.com/t/old-idea-revisited-datatype-as-antiform/2324/9

But I feel like there's some merit to it.

>> parse [1 /a 2 /b 3 /c] [some [integer! | ~{/word!}~]]
== /c

It's ugly, but it helps you realize "hey, this is being used as a datatype, here are the boundaries" so you're not going to get confused and think ~{word!:}~ is meant to be a part of an assignment of anything.

It might be able to be very efficient in ways a PARSE rule couldn't, and you could still name it out of the way:

>> refinement: ~{/word!}~  ; quasiform evals to antiform, fast match

>> parse [1 /a 2 /b 3 /c] [some [integer! | refinement]]
== /c

This is very speculative, but it's something I've been starting to think about.

But MATCH exists today as a way of using function arg spec dialect in PARSE.

1 Like