-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make null "falsey", can't assign null w/SET-WORD!/PATH! #816
Conversation
I am sure you meant Also I think it is necessary to explicitly state that |
This is a watershed change. Firstly, nulls are considered false by IF, CASE, ALL, ANY, DID, NOT, AND, OR, and other constructs. They no longer give an error from existing in the indeterminate state of "neither true nor false": >> if () [print "now, falsey"] ;-- no result That opens the doors to wider use of NULL as the uniform result for "not found" or "no match" (this has also been called "soft failure"). Due to a progressive evolution, some routines had used BLANK! for this purpose (ANY, FIND, MATCH, etc.) while others were using NULL (SELECT, IF, CASE, etc.). Now it is uniform: >> find [a b] 'c ;-- no result >> any [false false] ;-- no result >> all [true true () true] ;-- no result >> match integer! <some-tag> ;-- no result This uniform behavior opens the doors to using routines like ELSE and ALSO with any routine that has "soft failure", as well as making it easy to opt-out of operations without winding up with stray blanks: >> data: copy [a b c] >> append data all [ 1 < 2 1 > 2 'foobar ] == [a b c] ;-- previously this would be [a b c _] >> x: all [ true select [a 10 b 20] 'c ] else [ first [d e] ] == d >> pos: <not-found> unless find [a b c] 'd == <not-found> As a general policy, NULLs are "de-stigmatized" in cases like APPEND, INSERT, COMPOSE, PRINT, etc. Since they are not ANY-VALUE! and cannot be stored in blocks, they are reserved for the special intent of "no value was intended". BLANK!s on the other hand are now committed to be actual values which get appended or composed: >> data: [a b c] >> append data _ == [a b c _] >> compose [a (_) b] == [a _ b] However, this commit brings back one aspect of "stigma" for NULLs in that you cannot directly assign them via SET-WORD! or SET-PATH!, which was true of UNSET! in R3-Alpha: >> x: () ** Error: Needs value But with the widened scope of NULL usage, this means many things that would not cause errors in R3-Alpha now will: >> x: all [10 false] ** Error: Needs value >> pos: find [a b c] 'd ** Error: Needs value While this may seem like a drawback at first, it is actually a benefit. With NULLs being casually accepted by APPEND and other places, having them raise an error here is doing so at exactly the right point...it is a "hot potato" in the sense of saying *something must be done with it before it is stored*. The easiest thing to do is to use TRY to convert it to a BLANK!, or DID to convert it to a LOGIC!. >> x: try all [10 false] == _ >> pos: did find [a b c] 'd == #[true] In this way, erroring on the assign becomes an implicit assert. If you write `x: all [...]`, you are saying *all those things are true, and you want the last one*. If you say `pos: find data item` you are saying *it will be found*. Needing other reactions is specifically an expression of what you are doing if it's not. Making fixes to accomodate this change actually IMPROVE the quality of the code. Plus there are many other NULL-handling constructs now, which permit clever rewrites, e.g. if not pos: find data item [ fail ["Could not find" :item] ] ; ...can avoid null assignment with... pos: find data item else [ fail ["Could not find" :item] ] Most cases permit rewrites of this form. "SET/ANY" is brought back as SET/OPT, and specialized as SET*. >> set* 'x () ;-- no value >> set? 'x == #[false] Though a major step in the development of NULL, not all questions are answered yet. But this is definitely an improvement. Because it is such a major change, it requires heavy use of Ren-C features to bootstrap. This puts the nail in the (already decided) coffin of being able to bootstrap using R3-Alpha. The version of Ren-C that is being used is a year-old snapshot that has been working fine, and is almost certainly more stable than R3-Alpha...so this is not a problem. As a consequence, the shim code is cut down to just what is required to bring that Ren-C up to modern standards. %rebmake.r is changed from being a module to just run with DO, to ease the inheritance of the shim functions--which can't be changed without risking breaking the mezzanine code of the boostrap tool.
I am of the opinion that this is the overwhelmingly correct answer to age-old questions like "should select [a 10 b 20] 'c return BLANK! or null." It seems to square up a lot of things that were head-scratchers, such as whether BLANK! should dissolve in COMPOSE or not (now we know, it shouldn't) or whether appending null to things should be an error or a no-op (now we know, it should be a no-op). In chat I mentioned that I feel it is forgivable to have not been able to jump directly to this point. There were a lot of things floating in the mix--like people wanting to do a PRINT in the middle of either an ANY or middle of an ALL, and have it not disrupt the logic. When ELIDE didn't exist, it was easy to confuse this as being the mission of void, and until we had ELSE and new-UNLESS and all the routines that make working with NULL easier, this also wouldn't have seemed as viable. Though it's been informally true for a while, I've marked this as the "point of no return" on using R3-Alpha to bootstrap. The committed Ren-C we've been using is now just over a year of usage, and it has been stable enough. No super big problems...though it does have a couple of bugs here and there. But fewer bugs than R3-Alpha, and more tools for working around them. This allowed the compatibility shim to be streamlined, pretty much down to mostly the change To mitigate slowdown on those basic primitives I've done things like compose the ACTION! values into the function bodies so there's no word/path lookup. But there needs to be time spent on general study of what's taking up the most time in the make prep step, and how to speed it up while continuing the direction of making it less code and clearer to read. |
This is a watershed change. Firstly, nulls are considered false by
IF, CASE, ALL, ANY, DID, NOT, AND, OR, and other constructs. They no
longer give an error from existing in the indeterminate state of
"neither true nor false":
That opens the doors to wider use of NULL as the uniform result for
"not found" or "no match" (this has also been called "soft failure").
Due to a progressive evolution, some routines had used BLANK! for this
purpose (ANY, FIND, MATCH, etc.) while others were using NULL
(SELECT, IF, CASE, etc.). Now it is uniform:
This uniform behavior opens the doors to using routines like ELSE and
ALSO with any routine that has "soft failure", as well as making it
easy to opt-out of operations without winding up with stray blanks:
As a general policy, NULLs are "de-stigmatized" in cases like APPEND,
INSERT, COMPOSE, PRINT, etc. Since they are not ANY-VALUE! and cannot
be stored in blocks, they are reserved for the special intent of "no
value was intended". BLANK!s on the other hand are now committed to be
actual values which get appended or composed:
However, this commit brings back one aspect of "stigma" for NULLs in
that you cannot directly assign them via SET-WORD! or SET-PATH!, which
was true of UNSET! in R3-Alpha:
But with the widened scope of NULL usage, this means many things that
would not cause errors in R3-Alpha now will:
While this may seem like a drawback at first, it is actually a benefit.
With NULLs being casually accepted by APPEND and other places, having
them raise an error here is doing so at exactly the right point...it
is a "hot potato" in the sense of saying something must be done with
it before it is stored. The easiest thing to do is to use TRY to
convert it to a BLANK!, or DID to convert it to a LOGIC!.
In this way, erroring on the assign becomes an implicit assert. If
you write
x: all [...]
, you are saying all those things are true,and you want the last one. If you say
pos: find data item
you aresaying it will be found. Needing other reactions is specifically
an expression of what you are doing if it's not. Making fixes to
accomodate this change actually IMPROVE the quality of the code.
Plus there are many other NULL-handling constructs now, which permit
clever rewrites, e.g.
Most cases permit rewrites of this form. "SET/ANY" is brought back as
SET/OPT, and specialized as SET*.
Though a major step in the development of NULL, not all questions are
answered yet. But this is definitely an improvement.