I did a quick full skim-through of Carl's Rebol Blog, which split off into a second blog to discuss technical issues in R3-Alpha.
The second blog kicked off on April 6, 2006:
http://www.rebol.net/r3blogs/0001.html - "Welcome to the REBOL 3.0 Front Line..."
It got up to 352 entries, tapping out on February 20, 2011 (which was prior to the open-sourcing):
http://www.rebol.net/r3blogs/0352.html - "Relative speeds from compiler optimizations"
Again, I'm Skimming It For Anything Useful
However there is a lot more here to react to. This could take days!
But I do think it's worth it, in part just to kind of help put together a big picture of where this project was and where it is now.
-
Introduction of TYPESET! - Oh... Rebol2 didn't have them? Well, neither does Ren-C... typesets are replaced by type constraints.
-
MAKE Function Used To Be Variadic - I don't think I'm a fan of MAKE being variadic, but I have questioned the idea that there even need to be a MAKE FUNCTION!, vs just having FUNC be an arity-2 native to start with. This actually may be more prescient than I imagined, as there is no function datatype any longer... functions are just antiform FRAME!s.
-
To COPY or not COPY - I've sometimes critiqued Carl for seeming to have not confronted Rebol's actual core flaws head on. But here he does...mentioning new users getting bitten by mutability -and- also the problems from mutable binding on a block being passed in to two different MAKE OBJECT! calls. I've been satisfied with Ren-C's answer to the first issue by the CONST implementation--which has held up super well for years now. And how object creation works is undergoing a renaissance as I type this...no mutable binding (or even virtual blanket binding) required.
- It turns out there was a period in R3-Alpha where it was deep copying object specs and people eventually complained. He says he was waiting to see if anyone noticed. I do think it goes to show how little truly sophisticated Rebol code there was in practice, for people to not be able to tell sooner.
-
Hot Errors Removed - He says "These hot errors turned out to be overkill, and the benefit of error locality was offset by the difficulty of handling error values in general." But in effect this is another decision where Ren-C went more in a Rebol2 direction, introducing a whole new antiform error design.
-
No Out Of Range Errors With FIRST, SECOND, etc. - Because an early development of Ren-C was to make FIRST, SECOND, etc. specializations of PICK... I'd always thought of them as being related. Apparently at one time they were not. In any case, the issue of range errors has been something that's crossed my mind... and I think I'd rather you have to say
try fourth
in order to tolerate out of range errors. But the unification of PICK mechanics and TUPLE! selection, e.g.block.3
is the same aspick block 3
throws a wrench in it. This is still an open question. -
The 64-bit question - Wow, this is still not decided. Ren-C still uses 32-bit indices (but stores them as
int_fast32_t
etc. so they'll use 64 bits of space if the registers make it faster). It hasn't been an issue at the forefront. I'm more interested in distinguishing in the code distinct datatypes for 1-based "Index" vs. 0-based "Offset" and making sure those are distinct types that are harder to mix up. -
JIT Binding - I've never heard of this and don't believe I saw any code for it. On the surface it sounds like a relative of virtual binding, but it mutates the bindings in the block permanently and just sounds like something strange that never saw the light of day.
-
Debug Hook - Here he talks about being able to essentially break into the console at the moment of a FAIL and inspect the stack. One thing about historical Rebol is that because error trapping was frequent (e.g. ATTEMPT etc.) and being used as a programming style, you'd be getting a lot of false hits. Ren-C's error handling model is such that if you handle an error that is generally done by reacting to a RAISE before it's actually promoted to a FAIL, so you would not be getting broken into the debugger constantly in the casual operation of your code. We do need this. I believe in my heart that I am being attentive to the needs of a debugger, but there some more giant issues to knock down first.
-
CLOSURE functions - nope, all functions are closure-semantics now...without the crazy overhead of deep copying and binding the body of a function on every call!
-
ALIAS - Old ALIAS was removed long ago, because it was crazy. I've written about reviving this idea more reasonably for modern times.
- When Carl wrote about his feelings about removing ALIAS, he said "Perhaps you've seen a change in my attitude... considering how long it's taking to get R3 out the door!"
-
HASH! - I covered this in the other blog response, but the comments here might be useful. He talks about it in a later post as well. And then again, offers some conclusions
-
Getting More Information From FOR-EACH - Asking for not just the item but the series position is one idea. But coming up with ways to ask for more information in enumerations has been explored a number of places. I've wanted to be able to enumerate and know things like "is this the last element" or "is this the first element" and be able to check that easily. @hiiamboris has a fancy FOR-EACH that if you use a refinement in the spec it assumes you want the index at that position, etc. These are interesting ideas, so definitely want to look into them.
-
OBJECT! Field Type Constraints - I've wondered about this. It's definitely the case that languages which don't support type information wind up having another language layered on top of it that does (e.g. TypeScript). But this was never in R3-Alpha, and hasn't been a priority. I note he also mentions this kind of type constraint in module specs.
-
! and != - I was never a fan of adding
!
but we do support!=
My biggest problem is that != and == are a pair in C, but not a pair in Rebol: != is paired with = for lax equality, and !== is paired with == for strict equality. That makes it more confusing than anything, and I've meant to resolve this but it's another thing that's slipped. -
Considering Setters - I've mentioned in the past that Ren-C might have an answer to this, where you could use something like
obj.member.
to get at the "real" value bypassing the functions that assign them. There's a big bag of tricks in Ren-C to implement these kinds of features, but seems there's always something more foundational to attack. R3-Alpha had no code for it, though Red has ON-CHANGE* to react to assignments. -
Bye Bye System Object? - The idea of making changes to Rebol to support multitasking the way it was being done was fundamentally flawed. Needless to say the system object is still there (though the "system context" is now SYSTEM.UTILITIES, or SYS.UTIL for short.)
-
Should All Functions Return Values - Returning an UNSET! was a "value", just an ornery one historically, e.g.
x: print "hello"
was an error in Rebol2 and it remained one in R3-Alpha. Though it has been debated, in Ren-C PRINT returns NOTHING (the moral equivalent of UNSET!) and is legal to assign to a variable...it just unsets it. The only thing that you can't assign is an empty block antiform (a multi-return parameter pack), because when empty it is considered to be holding no values. This is called NIHIL and it is rarely returned.- So... the answer here is "kind of" and the only remaining question is should NOTHING be considered meaningfully truthy or falsey On a later blog there was a vote taken about how functions treat UNSET!
-
Leaky Functions - Not knowing this discussion was taking place in May 2006, I made a similar argument here in September 2019. But there's an additional wrinkle that Ren-C has unique customizable per-function RETURN definitions. So it's pretty much a slam dunk to make the change, and introduce LAMBDA as both returning its body result and not having a RETURN.
-
BIND Expands Contexts - Carl says "A few of you will ask: why does this matter? The main reason is memory (something we are optimizing on R3.0). Why force allocation for "free" variables (module variables) that are never used?" Everything is different in Ren-C, though it does deal with something called "Attachment Binding" where module variables actually are not allocated until they are used.
-
Current Module - I actually thought he was speaking about there being something available in a module similar to a SELF or THIS, which seemed like a pretty good idea. But instead he's talking about running DO on something and it expanding the module you called it from. Ren-C doesn't do this, DO runs its code isolated and can only give you back its final result. You have to use IMPORT if you want to bring in new definitions to the module.
-
UNICODE Support - It is interesting to see the considerations here, where it seems being able to load old LATIN-1 scripts was a concern. No consideration of UTF-8 Everywhere is given for storage, but that's what Ren-C does. He's wary of allowing UTF-8 in scripts themselves: "Another possibility would be to allow UTF-8 encoding within strings in the source code. The advantage is that you will be able to view the strings in the appropriate editor. The disadvantage is that the script would contain a range of odd looking characters." I have thought that being able to limit what Unicode is permitted as a kind of security measure might be important, but there are higher priority issues.
-
"Scant" Evaluation - FWIW, I do not really believe in the premise of scant evaluation. I think if you do something like
construct/only [foo: true bar: 'f]
then you get FOO as the WORD! true and not the logic, and you deal with it with tests like== 'true
. I guess I have to see more examples, but I've only seen this in module headers and I prefer the you-get-what-you-see version. -
At a Class Crossroads - I just brought this post up recently.
-
Overwriting System Functions - He mentions the general problem that since modules import their own copy of system functions under a word in their own variable space, they won't see changes made to the variable in the system. He talks about making some kind of "search and replace re-set" that would find all those imported stubs and fix them up. I've been thinking in pursuing this through allowing variables to alias other variables, so what you import starts out by default as an indirection and sees changes in the original. But sSo far, if you want to hook a system function, in Ren-C, you have the option of HIJACK-ing the identity of the function value itself
-
Free Variables in Modules - I dislike these but the codebase has not been retrofit to not use them. Only LIB and SYS.UTIL are currently enforced to not be able to create them. But I think you shouldn't be able to by default anywhere (e.g. "strict mode" in JavaScript).
-
Source Reflection Returning Unbound Copies - Again speaking somewhat from a security standpoint, Carl points out that if a module gives you a function you can get at its source code, and if you can get at its source code you can get at its bindings, and if you can get the bindings then you can reach the data. I think being able to get the source code itself would be problematic in this adversarial situation--with or without the bindings. It would be nice if some day people go over the language and make it secure, but I think we're well into the phase where making it good and interesting is far more important.
-
Should a Module Be Loadable More Than Once - e.g. if you are using different versions as dependencies of other different modules. Hmmm.
-
Do We Dare Add ++ and -- - Ren-C addresses this with ME and MY. e.g.
counter: me + 1
,counter: me * 10
. It fetches the value of the SET-WORD! on the left and substitutes that for ME. It's a lot nicer, and saves ++ and -- for weird symbolic purposes. -
Deprecating CONTEXT - He's opposed to it because CONTEXT creates an OBJECT!, but there's no such thing as a CONTEXT! datatype. I'm opposed to it based on it being a noun. I'm thinking WRAP might be best applied to what this does.
-
How Best to Blockify? - He proposes BLOCKIZE for what I call ENBLOCK (e.g. envelope/enclose in a block). BLOCKIFY is something where if what you have in your hand isn't already a block, it will wrap it it one.
-
DIR? - I came to be of the belief that all directories must end in a slash, and we enforce this systemically. Hence DIR? is just a question of whether a FILE! ends in a slash or not.
-
PORT! Redesign Objectives - This probably belongs in the What is a PORT! discussion. More to add there:
-
Lexical Exceptions in LOAD - I'm against finding a way to load e.g.
1st
. Ren-C gives you1.st
if you want it. This isn't a good investment of energy. -
IO Devices in R3 - And all of the Device Model is happily gone. We use libUV now, and it's a lot better while still being standard C, cross-platform with many more features, and maintained by other people.
-
None Propagation - Ren-C has VOID-in-NULL-out and it's working splendidly.
-
Explicit Evaluation Terminator - After a failed attempt to convince people that vertical bar
|
would be BAR! and serve this purpose, we have COMMA!, and it has turned out to be a lot better. -
R3-Alpha Released to Select Developers - This was June 2007. The Beta was expected on July 15, 2007, then delayed (?!) Well there never was a Beta.
-
Changes in MAKE FUNCTION! - Related to this, I just wrote a sort of new Dialected MAKE Manifesto
-
WITH and IN - I do admit to liking the
do in context [...]
construct. The IN construct is completely virtual binding now. BIND is still around but uses have been dropping as more and more of the codebase is able to work with virtual binds on top of largely unbound blocks of code, "viewed" several different ways without touching the binding bits. -
UNSET! As An Argument - R3-Alpha got fairly permissive, e.g. allowing comparisons of UNSET! to other values. I've brought up that I think it may be a mistake to allow things like comparisons to take NOTHING, and offered justifications for that.
-
Modules Ripping Carl Apart - "I have to admit that so far modules have been, well, too much like taming a tiger. And, so far, the tiger has been winning... often, ripping me to pieces. The tiger is the main reason R3 is running late. Yes, blame the tiger. I've got many scars."
- In 2010 he blamed them again for another release delay: "Battling the Module Monster, Again"
-
Admission that Unicode Was Harder Than Thought - " So, Unicode is the focus of our current development, and it must be clearly stated, this is a non-trivial project. Our goal is to have it ready for initial testing by the end of the month. I'll admit that we under-estimated the magnitude of the Unicode project..."
-
R3 Alpha Public Release - This was in January 2008. My first encounter with the Rebol language was approximately April 2008, and so I didn't use Rebol2 at all... believing from the community that Rebol3 was the hot new thing. I didn't realize it had only become public that recently, because everyone else who was involved and talking about it was on the inside track I guess. I don't know if this timing makes me fortunate or unfortunate, but it certainly explains why I never got attached to a Rebol GUI.
-
ISSUE! as a WORD! datatype - I disagree with this decision (as many did), and in Ren-C it is an immutable textual type..used as the implementation for characters as well (characters are just single-character ISSUE!s). I've proposed renaming these to TOKEN!.
-
BITSET!s - BITSET! is terribly inefficient and buggy, if you try to do things like XOR a negated bitset with another none of it works right. If your bitset is sparse (e.g. representing a Unicode character set, as one might do) then it will quickly consume megabytes. I did some work with Roaring Bitsets and linked them up as the implementation for bitsets, which worked well but I didn't feel like maintaining the dependency given higher priorities. But that branch is still around.
-
The ZERO? Question - I hadn't thought of this. POSITIVE? rejects non-numbers, NEGATIVE? as well, but ZERO? lets you test series as a simple synonym for
= 0
but then also considers 0:00 to be ZERO? Ugh. I like TRASH? (~) as a placeholder better than zero for many reasons, so the single-character-zero-filler argument doesn't apply. I think ZERO? should apply to numbers only and error on other types. -
STRING! is not BINARY! - Because R3-Alpha used variable sized encoding for strings inside the system, it couldn't offer you a usefully invariant binary view of strings. Ren-C can do it because it uses UTF-8 Everywhere, and if you're looking at a BINARY! alias of a TEXT! you can make modifications only if that atomic modification leaves the result as valid UTF-8. You can also alias WORD!s as TEXT! or as BINARY!, but it will be an immutable view. The benefits inside the system of sticking to one string encoding are innumerable and well worth all the work.
-
Script Error - Hadn't really thought about it but he has a point here. Something to consider in an ERROR! redesign, if such a design ever happens.
-
Pruning Down READ and WRITE - Despite earlier assurances that READ and WRITE give BINARY! and it's LOAD that does the fancy work, this shows some of the confusion slipping in about what READ is...and that's what provoked my own confusion at things like READ/STRING and READ/LINES and READ/AS. Red has these refinements too. It's all quite confusing, but Ren-C has the advantage of letting you do
as text! read %some-file.txt
which gives you a mutable alias over the UTF-8 data with no loss of efficiency. I haven't killed off READ/STRING or READ/LINES but now I feel I really should... that's LOAD's business to have some convenient syntax for. (Dialected?load %file.r
orload [<text-lines> %file.txt]
) -
VID Expression Optimization - So this is the first time I've felt there was something to comment on in a GUI post, since it's dialect philosophy. I started a thread on it
-
MAP! Indexing - Again, this is relevant to the determinism I've been discussing, and it's interesting to see there was questioning about it.
-
Multicontext Variable Lookup - Like it or not, this is what we have with Virtual Binding.
-
Find on OBJECT! - He mentions using IN. But with the binding logic of virtual binding, IN has to return the original item unbound if it wasn't found in the context... so it doesn't give a good answer. Ren-C uses HAS for this (null if a word not present, vs. the unbound word) and disallows FIND on objects. Use HAS.
-
RFC: func-local, funclo, funco, funo, funx - Wow, I didn't know the name considerations were so bad...before FUNCTION was chosen. Ren-C has virtually-bound LET and has dismissed with the bad idea of locals-gathering functions that gather all SET-WORD! as variables. That is not coherent... SET-WORD!s are used in places where they should not create variables (object keys, other dialects). So this idea is dead and will only be resurrected as a toy for code golf competitions or similar. And FUNC and FUNCTION are slated to be synonyms, as all abbreviations are intended to be.
-
Data Conversions that MAKE Sense - This definitely needs to be thought about, but also to eliminate needless duplication between TO and MAKE. I've written about the differences and the possible rules.
-
PARSE Project - While a lot of this has gone offline, I did preserve the remarks from the wiki on a Trello board (that seriously needs updating, now!). Sigh. Hurry up AI, I need you to edit all this for me.
-
Angry Comments About A30 Not-Quite-Public Release - Here it's January 2009. Rebol 3.0 was certainly positioned in a promise-making and not-delivering way, and Red followed that pattern. Part of what keeps Ren-C from being completely exhausting is not doing that.
-
Inklings of REWORD - It seems the REWORD we have today started with this discussion, right on the heels of the previous feedback also in January 2009. The design, authorship, and testing of these things simply add up to so much time... it should have been obvious to anyone reading this at the time this would never be finished. (I wasn't reading it, I didn't get involved until Rebol became open source in 2012...)
-
PICKing Negatives and Zero - Have to admit I haven't thought about this at all in years. Ren-C reverted to the Rebol2 behavior. In practice, I don't think it comes up enough for people to care.
-
Weird ASSERT/TYPE Refinement - I didn't like it and didn't see the point, so I killed /TYPE. Ren-C lets you do ensure object! spec and ensure [~null~ tuple!] spec.version ... you can do your own FOR-EACH enumeration over a block of type/value pairs if you have enough of them to matter.
-
Isolated Namespaces In Modules - Turning on isolation was horribly bloated, but Ren-C has solved it efficiently and makes it the default for all modules.
-
May I just say that the code for all of the module stuff--especially isolation--is convoluted and buggy with equally buggy usermode portions, and it literally made me give up trying to use my 2018 bootstrap executable for the FENCE! conversion. I had to go gut the module system to make enough simplifications that it would work at all, and am still dealing with the issues of deploying new cross platform binaries that will serve for at least another year or two.
-
If you're wondering "why not just use a modern Ren-C for bootstrap" the answer is that in most areas unrelated to modules--e.g. places where I worked on hardening it--I can rely on it more than the dark corners of modern executables. Bootstrap is rather demanding. Also right now has various in flux instabilities and bad performance.
-
-
DECODE and ENCODE - I never really got the split in responsibilities between ports and codecs. We're told that if you want to do streaming hash calculations in chunks on large files to use a PORT! to do it. But then DECODE and ENCODE aren't light wrappers over a chunking port but rather some very naive functions. Trivial design that was used as an excuse to make some more weird monolithic C code... there's no architecture, it's just a function table.
-
Read Only Strings, Blocks, and Objects - So apparently PROTECT came along in 2019. The interesting thing about Rebol being a C codebase with limited type checking is that it was scout's honor for all the code in the system to remember to check the protection bits before making a modification. Ren-C can build as C++, and enforces the checking of mutability bits before changes with the type system...so if anyone doesn't use a check routine to transition from const to mutable in the C, then when building with C++ that will be caught. I'm quite proud of that...and it has been working without bugs enforcing CONST as well as PROTECT (and evaluator holds, etc.)
-
PROTECT/HIDE - While I thought this was kind of cool when I first saw an example, it turns out that this is another features that was added on a whim without a systemic review of the implications. But for this case I haven't come up with a way to really make it much other than the scout's honor I describe from R3-Alpha's basic protection bit. I've not killed the feature because hidden bits are used to various effects in things like specializing fields out of frames, but I'm really not sure if it makes sense in other cases.