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!. This evaluates much like a plain GROUP! in a PARSE...but instead of the result vaporizing...it 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]]
fedcb
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).
UPDATE: This has been hit with a bullseye by UPARSE...and the problem of calling normal functions while passing them captured parse material is covered by the ACTION! combinator
>> print-reverse: func [x] [print reverse x] >> uparse "aaabcdef" [3 "a" print-reverse/ [across to <end>]] fedcb
Yes, that actually works in UPARSE!
Core: open problems of the object design
Tip of the iceberg of the problems with the model. 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?
N/A.
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 "heavy nulls" are an idea well worth it to ensure that pure NULL keeps its unique status for representing break.
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]
)?
N/A
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!
UPDATE 2022: Ren-C has an answer with "Definitional Failures"
When originally writing this post in June 2020 all I said was that it was clear "that conventional error-trapping should not conflate things that arise from "typos" with explicitly raised errors".
But I'm proud to say that yet another difficult question has been resolved with ^META/isotopic solutions (which during my brief conversations with Boris he did not appreciate, and I felt the dismissive attitude to another member of the Red audience was not worth trying to push through, vs. other uses of time).
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?
N/A
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.
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 outlawed...as 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).
(Overall this ties into great work done in Ren-C on PATH!, which includes generalizing REFINEMENT! so there isn't just /a
but also a/
and a//b
... a BLANK! in a PATH! is simply not rendered. The immutability of paths means that even though /a
is a 2-element path with a blank in the first slot, it can still be represented in one cell and not worry about being unable to handle mutations. See Heart Bytes)
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?
Core: what should empty any []
and all []
return?
UPDATE 2022: both Empty ANY and ALL return void. When this post was originally written it was just ALL, and ANY returned null. But it turns out to be so high leverage compared to the tiny benefit of making ANY null that we have to do it. The proof is in the examples!