Red's Design Issues Wiki

Here's a page posted on Red's GitHub:

Red Design Questions Wiki

I'm bookmarking it here, along with some quick review notes.

Vector & matrix DSL design

No real comment, other than it's interesting that the order of the page ranks things as "most influential"
While in Ren-C, R3-Alpha's VECTOR! has been removed from the core (you can build a Ren-C without it). Hence it isn't one of the built-in types identified by a byte in the header. So it's a primordial test of a custom type, and that is the main lens through which its relevance is considered.

I'm sure if they want plenty of opinions on VECTOR! they can talk to Oldes.

Parse DSL: simplify fail rule to [end skip] , break / reject to return success/failure from the loop (while/any/some) , break now as an emergency exit from the loop, bring also into parsing

END SKIP as a way to force a fail is an amusing idea. But much improvedly, Ren-C just gives meaning to LOGIC! true in PARSE as "keep parsing" and LOGIC! false as "fail here". (See below for cool ramifications of that.) So you can fail just by saying FALSE. It takes away from the ability to match LOGIC! values literally, but you can't match INTEGER! values or BLOCK! values literally without a "QUOTE". And with the generic quoting Red argues against you've got more convenience in that area, e.g. '#[true] or '[a b c] or '3.

It is good for naming reasons, too since FAIL has very specific meaning in Ren-C (way to raise errors, fail "msg" is nicer than do make error! "msg" and provides a dialecting opportunity).

The rest I'd have to look at in more detail to have an opinion on.

Parse DSL: rules with arguments

If I read this right...Ren-C has all this and more with GET-GROUP!. :boxing_glove: This evaluates much like a plain GROUP! in a PARSE...but instead of the result dynamically composes itself into the parse stream. That makes it a full superset of PARSE's unusual IF...since in Ren-C LOGIC! true means "keep parsing" and LOGIC! false means "rule fails". Thus :(mode = 'foo) gets you the continue-or-not you want. But far more open-ended than that, especially since NULL vaporizes and continues the PARSE as #[true] would. It's all kinds of keen:

GET-GROUP!s in PARSE mean execute-and-splice-as-rule

So if you want a dynamically generated rule as a function, just say :(my-rule-maker arg1 arg2) and it will be spliced in.

I consider that bit solved. But the part that isn't solved is saving you the trouble of capturing material to pass separately...kind of as if you could extend PARSE with your function acting kind of like a keyword. Imaginative pseudocode:

 >> print-reverse: parse-func [x] [print reverse x]
 >> parse "aaabcdef" [3 "a" print-reverse [to end]]

e.g. the "argument fulfillment" phase is using parse rules--and not the evaluator--to do it. But where I lean in this direction is asking about whether there is a model by which PARSE can be extensible in the same way DO of a BLOCK! is extensible... some kind of analogue to regular functions that lets you batch together "native" PARSE extensions (like THRU or INTO) along with user-added ones that speak the protocol (as if someone could add COLLECT after-the-fact).

It's something I think about while rewiring the evaluator for stackless processing, where the same stackless "Trampoline" bounces states without deepening the C stack for both PARSE and the general evaluator. I'll have more to say about it when I understand it. :-/ But it plods along.

Core: open problems of the object design

Tip of the iceberg of the problems with the model. :ice_cube: While Ren-C inherited the same object-model madness, techniques like generic quoting are making inroads representationally, and things like derived binding are aiding classic problems:

"Derived binding is an interesting design that overcomes a serious weakness of historical Rebol combinatorics (where 1000 instances of an object with 50 member functions each had to make deep copies of the bodies of each function so the words in the bodies would point to the instance, so you're making 50,000 deep copies just to create those 1000 objects)."

For a simple example, see "O-Big"

Core: efficient vector arithmetic

Regarding their "Adding a number to a vector changes the vector itself #2216", I have proposed a concept whereby operations like ADD and MULTIPLY follow Rebol's "mutate by default" rules, and PLUS (infix alias +) and TIMES (infix alias *) finesse the idea of not mutating. These ideas originated in design for efficient BigNum arithmetic, which shares many of the same issues.

Devil is in the details there, but it proposes building things on a model that is foundationally mutating.

VID DSL: automatically bind (literal only?) actor & reaction bodies to the face?


Core: loop return values

Ren-C has a systemic loop return result protocol. All loops that BREAK return NULL, and there is no BREAK/RETURN. If you wish to violate the protocol, you do so with THROW to a CATCH.

This is a helpful protocol for building your own loop constructs out of other loops...without having to reach in and figure out how to hook their BREAK. Being able to tell from the outside if the loop was interrupted far outweighs the idea of BREAK/RETURN, and "voidifying" null loop body results is worth it to ensure that unique status for NULL.

Core: behavior of series access outside the data boundaries

I talk some about this in "Where the Series Ends" (for you young folks, that's a Shel Silverstein reference).

VID DSL: should it allow to override already defined actors (e.g. base on-down [probe 1] [probe 2] )?


Core: how to solve inelegancies and dangers of error? try , attempt and catch on arbitrary code?

As usual, it is hiiamboris asking the good questions. Don't know the answers, but I'll offer a thought here... that conventional error-trapping should not conflate things that arise from "typos" with explicitly raised errors:

red>> attempt [print "blah" do make error "On purpose!"]
== none

red>> attempt [prnt "oops" print "But I didn't mean to raise an error"]
== none  

So beyond Boris's example, it shows that the already sketchy nature of error handling via exceptions is turned into even more of a minefield in Redbol. Again: I don't have answers, I just appreciate the questions.

Web: how should cached versions of remote files be named (considering user-friendliness, pathname length, sanitization of invalid chars)?

Beyond the scope of current concerns, and likely not all that relevant as the main target is Wasm.

Core: should operators use the result from funcs with literal arguments? (if f: func [:x][x * 2] , should f 1 + 2 be equivalent to (f 1) + 2 ?)

Yes. I won't delve into details on this esoteric (-seeming) point...other than to say that several features I'm very pleased with depend on this behavior. (For example: soft-quoted branching.) I find no compelling points in any counter-argument worth sacrificing the related features.

Core: should percent type allow scientific notation, and should it be constrained in range?

Don't care.

VID DSL: should panel face draw a text facet on it?


Core: do we want /deep refinement for take ?

Doesn't seem like the worst idea. But I think the better angle is just to make sure that even if TAKE didn't have it, that someone who wanted it could make it easily. So features like AUGMENT are geared in this direction.

Historical questions & explanations

Arguments on why paths evaluate picked items (so-called active accessors)

It's a reasonable-sounding argument that it is "unlikely" that you want block/1 to run the first function in a block. But mechanically it's the simpler-seeming rule. I guess it folds into a large part of the sketchiness that is path-processing. :man_shrugging:

I will say that Ren-C's "get-pathing" and PICK-ing, and "set-pathing" and POKE-ing, are unified and run under the same dispatcher. So :path/1 and pick path 1 are the same, and :path/x and pick path 'x are the same, for whatever those things happen to be. There's not a separate codebase for getting paths from PICK or setting paths from POKE. Hence it's called "path picking" and not "path selection".

Command line argument parsing rules

This previously crossed my radar. If they do the work of writing test cases, sounds great. Better them than me.

Why word and a single-word path are different (despite the visual similarity)

They shouldn't be, and single-word PATH!s should be WORD!s with all spaces are. Ren-C has implemented PATH! immutability (at the "top level")...which isn't that unprecedented, as it makes them more parallel to TUPLE!. By making paths immutable, it's possible to enforce a set of rules on them at the time of their creation (e.g. no PATH!s in their top level, that aren't inside GROUP!s, no FILE!s or URL!s, just GROUP!s/BLOCK!s/WORD!s/INTEGER!s etc.).

If this sounds constraining, consider the WORD! analogy again. Making a PATH! with a PATH! in it like having a WORD! with a slash in it. When things are immutable you have a moment of creation to enforce your check and then you just don't worry about it. It works for WORD!, why not PATH!?

(You can still cheaply alias PATH!s via AS to get a BLOCK!...but the BLOCK! you get is simply read-only. With "UTF-8 Everywhere" you can also alias WORD! as a string via AS, but once again the resulting value will be read-only. You can even alias them all as BINARY!, as in Rebol2. :-P)

The idea that PATH!s are some kind of generic "ANY-BLOCK!" is not all that compelling. DocKimbel himself has complained about people wanting long or multi-line paths. If you really want to mutate a path freely and promise not to end in a bad state, then COPY AS BLOCK! your path...muck with it, and then AS PATH! it (which will mark the underlying array read only, and do an integrity check that you didn't reduce it to one word).

(Note: This ties into my long-desired transition of REFINEMENT! into just being a PATH! without loss of efficiency, because the immutability allows /foo to be passed around in a single cell by value...though it enumerates as if it were a two element path with a BLANK! in the first position. A mutable path would require a node creation in case the path were ever expanded so that other sites could see it. While the work is still very much in-progress, it is promising, and opens doors to things like foo/ and /foo/(bar)/[baz mumble]/ if you want them.)

Core: :get-word function argument evaluation semantics: R2- or R3-like? (final?)

There absolutely has to be some way to literally get a symbol/value in the position, regardless of what it looks up to. I don't know if I'm in love with the notation for :x for unescapable quoting and 'x for escapable quoting, but that's a different question.

Core: how to allow maps to have none values?

Some day Red will realize how much they miss out on by not having NULL. I guess it's kind of like societies that never grokked the invention of zero.

NULL, BLANK!, VOID!: History Under Scrutiny

Core: how money datatype equality and comparison rules should work?

It's like the Joker says...

Core: what should empty any [] and all [] return?

NULL. But if you don't have that, I guess a NONE! will do.

1 Like

Excellent track record of anticipating and addressing the many historical inconsistencies and tarpits of rebol @hostilefork

1 Like