Brave SWITCH new .WORLD [?] - Skippable Predicates


#1

Note: This post was originally written to use REFINEMENT! for the predicates. However, generalized TUPLE! has seemed more favorable, so that’s what’s used when talking about it now…even though generalized tuples do not exist at time of writing.

It’s a bummer that all the Rebol functions like SWITCH or FIND use some kind of internal magic operator to decide if things are equal. We’d like that operator to be less opaque and more clearly documented and understandable, and it’s in the long to-do-list… to try and figure out how it ties into IS-ness or MATCH-ness or whatever it is.

Still, no matter what the default is, we know we need a way to override it. Ren-C had added a /STRICT refinement to SWITCH to deal with this case:

>> switch 0.01 [1% [<pretty lax...>] 0.01 [<stricter>]]
<pretty lax...>

>> switch/strict 0.01 [1% [<pretty lax...>] 0.01 [<stricter>]]
<stricter>

But the name “strict” wasn’t something super agreed upon. And it’s just the tip of the iceberg, you kind of want to pass in more functions. That seems kind of ugly.

>> switch/compare 0.01 [1% [<pretty lax...>] 0.01 [<stricter>]] :==
<stricter>

Fancy New Experiment: Skippable Refinements that ARE the compare!

Dig this (and it will look even better when EQUAL? and = are strict by default, and IS is the lax operator…)

>> switch 0.01 [1% [<pretty lax...>] 0.01 [<stricter>]]
<pretty lax...>

>> switch 0.01 /strict-equal? [1% [<pretty lax...>] 0.01 [<stricter>]]
<stricter>

>> switch 0.01 /== [1% [<pretty lax...>] 0.01 [<stricter>]]
<stricter>

There’s a skippable slot for a “refinement” (blank-headed PATH!) between the thing you’re switching on and the cases block. And it looks up to an ACTION!.

It also accepts literal ACTION!s. You won’t/can’t type that in by hand…but that means if you’re doing an APPLY there’s a normal frame slot for your compare to go into. (It’s one of the nice things about skippable parameters being knownst to the system in a way that variadic parameters are not.) If you need a SWITCH variation with a different interface you can make one out of this one.

Notice that the interpretation puts the switched-on expression on the left or in the first argument.

>> x: 10
== 10

>> switch x /greater? [20 [<nope>] 5 [<yep>]]
== <yep>

Before we go propagating the idea all over the system, why not give this a spin. Kick the tires a bit. Write some tests!

>> x: 10

>> switch x /(reduce pick [:greater? :lesser?] 1) [20 [<a>] 5 [<b>]]
== <b>

>> switch x /(reduce pick [:greater? :lesser?] 2) [20 [<a>] 5 [<b>]]
== <a>

Does SET/ANY, GET/ANY-thing matter anymore?
#2

I feel like maybe this should be done with generalized TUPLE! instead.

switch obj/value /matches [...]

switch obj/value .matches [...]

Because the plan is that TUPLE!s will be inert, there’s not much odds of you switching on one literally. But PATH!s you will want to switch on.

As the approach goes on, this will make it harder to do things like FIND on explicit tuples

do [find data .matches integer!]  ; yay, works

do [find css-stuff .class]  ; oops, tries to call CLASS as the predicate

But with generalized quoting, you’ll be able to get around it:

do [find css-stuff '.class]

do [find css-stuff lit .class]

You just have to understand that TUPLE! has a purpose that is “effectively evaluative”, kind of like you have to understand when you go to find WORD!s. find data my-word already needs a quote or lit. It comes with the territory.

I’m a little bummed because it kind of feels like these “are refinements”, but I think that re-imagining it and saying tuples starting with dot act as predicates (by convention) probably has better bones.


#3

I find that tuple looks better, and wins when it comes to ergonomics.


#4

Using something different than refinement could also have another advantage, that it could be interpreted as a kind of chain. If we assume SWITCH usually goes with IS, you might want to use = for strict equality. You also might want to use a more literate “equals”. But there isn’t any particularly nice way to say “unequals”.

So what about just good old:

 switch x .not.equals [
     <whatever>
 ]

Then imagine if we’ve define IS as is: :equals/lax. So there’s a refinement on equals, and you might also call it as:

 switch x .not.equals/lax [
     <whatever>
 ]

So that’s an interesting potential advantage. Though the proposal I have for tuples is that paths take priority, so it would see that as the PATH! [.not.equals lax]. But if the chain is specializing that should still work, e.g. (=> NOT EQUALS) could produce a function that takes one argument and has a /LAX refinement.

(This is all pretty out there, and easier said than done…but it does tie into stuff I’d like to see be able to work. I’m just putting in another point of notational advantage for TUPLE! here.)

This also suggests the possibility that a GET-TUPLE! might have a purpose as a chaining generator, e.g.

 chain [:a | :b | :c]
      => get c.b.a 
      => :c.b.a

We also might consider if CHAIN should be reversed, which is something that came up before.