Skip to content
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

ANY and ALL treat unset! as a value, not as none! or false! #564

Closed
Siskin-Bot opened this issue Feb 15, 2020 · 1 comment
Closed

ANY and ALL treat unset! as a value, not as none! or false! #564

Siskin-Bot opened this issue Feb 15, 2020 · 1 comment
Labels
Backwards incompatibility CC.resolved Issue with CureCode status built, tested or complete Guru meditation Oldes.resolved Bugs/wishes with Oldes' fixes/features Red compatibility Issues and changes required for compatibility with Red language Ren.resolved Status.important Test.written

Comments

@Siskin-Bot
Copy link
Collaborator

Siskin-Bot commented Feb 15, 2020

Submitted by: Gregg

ANY and ALL only recognize none! and logic! values for their failure cases. Unset! slips through there as a value.

>> type? any [get/any 'c]
== unset!

Imported from: CureCode [ Version: alpha 32 Type: Issue Platform: All Category: n/a Reproduce: Always Fixed-in:alpha 38 ]
Imported from: metaeducation#564

Comments:

Rebolbot commented on Jan 25, 2009:

Submitted by: BrianH

This is how R2 works as well. I have always been surprised that it doesn't throw an error, as that is the main difference between the unset! and none! types.

ANY and ALL are primarily used as control structures. As such, it might be good for unset! values to be skipped by both ANY and ALL, like noops. Unset values would be treated as they are now for ALL (as not false), and converted to NONE by ANY before testing:

ALL [()] == unset!  ; current behavior
ALL [() 1] == 1  ; current behavior
ANY [()] == none  ; new behavior
ANY [() 1] == 1  ; new behavior

Any change to how ANY and ALL treat unset! would have subtle implications to a lot of code, and the affected code would be very difficult to find and fix. So now is the time to make the change, before too much code is written in R3.

This is being discussed in R3 chat, in R3/Language (21), and this request is waiting for consensus. This comment will be updated with the decision when one is made (or removed if another comment declares the decision).


Rebolbot commented on Mar 25, 2009:

Submitted by: Carl

Implemented as suggested above.


Rebolbot commented on Aug 17, 2015:

Submitted by: fork

UNSET! should be an error for these. They are convenient proxies for a chain of ANDs or ORs without the infix baggage (once crowd-favorite #1879 is in place).

@brianh says what we're all thinking: "I have always been surprised that it doesn't throw an error, as that is the main difference between the unset! and none! types." When there's no benefit, principle of least surprise comes into play. Consider:

my-function: func [a b ...] [...] ;-- accidentally doesn't return a value

if all [
    condition1 condition2 condition3
    my-function a b ...
] [
    print "Yay all conditions hold, let's roll!"
]

That's bad if UNSET! is treated as conditionally true.


Rebolbot commented on Aug 17, 2015:

Submitted by: MarkI

Firstly, your code example will fail as written, causing "** Script error: if does not allow unset! for its condition argument".

Secondly, "accidentally doesn't return a value" is a very misleading statement.
It means the same thing as "Accidentally returns #[unset!]." which is the same as

"Accidentally executed no code whatsoever, executed [return #[unset!]], or executed code that evaluated to #[unset!]."

That seems to be pretty explicit, and in fact very difficult to do "accidentally". YMMV, of course.

Thirdly, this proposal would make it a lot harder and uglier to put calls to PRINT in ALL blocks.
Again, YMMV, but some might find it unacceptable to be forced to phrase all such PRINTs as "also true print".

This is what I am guessing BrianH meant by his statement "it might be good for unset! values to be skipped by both ANY and ALL".


Rebolbot commented on Aug 18, 2015:

Submitted by: fork

Okay, I think I was wrong here. But the best argument for why I am wrong is actually one that hasn't been brought up yet, but which @MarkI reminded me of...which are the needs of expression barriers. Which has broader implications for other constructs that have a similar question of UNSET! ignoring. See #2248

But since ignore should mean ignore...it leaves me in disagreement that ALL [()] == unset!. Instead it should be the same as ALL []... hence TRUE. Though it doesn't probably come up terribly often, that's a more coherent behavior.


Rebolbot mentioned this issue on Jan 12, 2016:
ALL [()] should have the same result as ALL []
Should "interstitial" UNSET!s be ignored in "evaluative" contexts?


Rebolbot added the Status.important on Jan 12, 2016


Hostilefork added Status.important and Ren.resolved on Oct 10, 2017


Hostilefork commented on Jun 13, 2018:

The (hopefully final) resolution of this in Ren-C is that there is no such thing as an "UNSET! value", only a NULL evaluative result...and it is conditionally false.

>> if null [print "this doesn't print"]
; null (the standard protocol of failed conditionals)

>> if true [null]
; forced to VOID! to differentiate from failed conditional

Additionally, ANY and ALL, when they fail, return this NULL evaluative result.

 >> null = any [false]
 == true

 >> null = any [() () ()]
 == true

This is true of other things that used to return "valued" BLANK! on failure:

 >> find [a b] 'c
 ; null

 >> select [a 10 b 20] 'c
 ; null

 >> select [a 10 b 20 c _] 'c
 == _  ; a BLANK!, a value, again parallel to NONE!

Oldes added a commit to Oldes/Rebol3 that referenced this issue on Oct 7, 2018:
FEAT: making UNSET! as a conditionally "TRUTHY"


Hostilefork commented on May 31, 2019:

The (hopefully final) resolution of this in Ren-C is that there is no such thing as an "UNSET! value", only a NULL evaluative result...and it is conditionally false.

While this is still true...the development since is that there is an unfriendly value called VOID! that is neither true nor false. However, it is not directly related to the unset state of a variable (because if a variable holds a VOID! value it is not unset):

https://forum.rebol.info/t/why-void-is-not-like-unset-and-why-its-more-ornery/947

If one wants to slip a routine that produces a VOID! into a stream of something like ANY or ALL and have it ignored, you can use ELIDE which is an "invisible" function

ren-c>> any [
    false
    print "This is bad"
    10
 ]
** Script Error: VOID! values are not conditionally true or false

ren-c>> any [
    false
    elide print "This is okay"
    10
]
This is okay
== 10

ELIDE and invisibles are a generic mechanism that work in any evaluative context (e.g. CASE statements)


@Oldes
Copy link
Owner

Oldes commented Apr 14, 2020

Now it is again compatible with R2 and with Red:

>> type? ALL [()]
== unset!

>> type? ALL [() 1]
== integer!

>> type? ANY [()]
== unset!

>> type? ANY [() 1]
== unset!

@Oldes Oldes closed this as completed Apr 14, 2020
@Siskin-Bot Siskin-Bot added the CC.resolved Issue with CureCode status built, tested or complete label May 22, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Backwards incompatibility CC.resolved Issue with CureCode status built, tested or complete Guru meditation Oldes.resolved Bugs/wishes with Oldes' fixes/features Red compatibility Issues and changes required for compatibility with Red language Ren.resolved Status.important Test.written
Projects
None yet
Development

No branches or pull requests

2 participants