Open gafter opened 7 years ago
I support the idea but in my opinion not
would be much more readable than reusing the negation operator.
Just reusing @HaloFour examples:
if not(foo is Bar bar)
{
// stuff
}
while not(finished)
{
// stuff
}
do
{
// stuff
}
while not(finished);
And maybe as added benefit:
if (foo is not Bar bar)
{
// stuff
}
This can probably be applied to other constructs in the language..
I'm in favor of unless
but see @alrz explanation as to why this is more expensive to add.
Is this a new statement form or we are going to just remove parentheses from all statements like Swift/Rust?
Probably new statement form, Gafter showed in the linked issue that removing the parentheses could cause ambiguity.
I see, thanks.
I would just replace "!" with "not" because it's much more readable. I currently always write "== false" to clearify that condition is negated. Parentheses should be kept, as they mark begin and end of the condition especialy on multi line conditions.
My personal opinions:
if (foo is not Bar)
- π π if !(expression)
- π if not (expression)
- π I disagree with @eyalsk and @RogerWaters and actually think !
is more readable than not
for a simple boolean expression.
I'll throw my vote in for !(expr)
too.
foo is not Bar
only works for pattern matching, not other expressions where it makes sense to read it one way but you want the condition negated.
not
is an entirely new keyword that I don't think is necessitated here. We already have !
throughout the rest of the language, which is well understood. We don't have - or need - if (foo not == bar)
etc, or bool a = ...; bool b = not a
, so I don't see the benefit for an if not
.
I hope this will not be implemented in 7.x-8.x:
Why this and not e.g. better LINQ syntax integration? To show that incremental language features can be delivered quickly as part of minor versions?
The not
syntax - does it mean interchangeability with !
? It's not even in LINQ where it would make the most sense.
The !
syntax - will it cause confusion with the coming not-null assertion? Is it that much more readable even? Why not just extract meaningfully named variables and extensions? E.g. I use if (x.None())
instead of if (!x.Any())
.
@yaakov-h
foo is not Bar only works for pattern matching, not other expressions where it makes sense to read it one way but you want the condition negated.
Well, that's your opinion which I respect but I disagree, not
can definitely work here too, it's a matter of taste.
not is an entirely new keyword that I don't think is necessitated here. We already have ! throughout the rest of the language, which is well understood.
And this is a new statement, we didn't have when
before and now we do.
We don't have - or need - if (foo not == bar) etc, or bool a = ...; bool b = not a, so I don't see the benefit for an if not.
I'm not sure I follow, you wouldn't be able to use not
inside the if statement, only outside; like I said before we didn't have many things in the language and now we do, in my opinion it's really a weak argument against it, it's more reasonable to say it's not my cup of tea than to say we didn't have it before or try to speak in the name of everyone and say we don't need it, well, again, I prefer not
. π
I wonder whether if!(...)
works because some people prefer the following style if(...)
over if (...)
.
p.s. Just to make myself crystal clear, I don't mind reusing the negation operator, I just think that not
would be more readable but than @DavidArno thinks the opposite, fun. π
@eyalsk
I wonder whether
if!(...)
works because some people prefer the following styleif(...)
overif (...)
.
You'd still be able to put a space between if
and !(...)
here, e.g. if !(foo is Bar) { }
.
Personally I think that !
is plenty readable. Languages in the C# family generally prefer punctuation for operators rather than keywords and it would be weird to have both !
and not
be unary negation operators. That said I don't think that this proposal obviates the need and use of a not
pattern and the above could just as easily be written as if (foo is not Bar)
. But this proposal is significantly easier to implement and quickly eliminates a niggling aspect of C grammar.
I'm not enthused about this proposal but it's certainly an improvement over today's mess of parenthesis required to do this, if(!(condition))
.
My main problem with it is that it still forces negative is
expressions to be inside out. Compute the value of some expression and then negate it, rather than just conceptually checking for the negative expression. This makes it a lot slower to mentally parse what's going on. Thus, I'd still prefer isnot
(don't care the exact wording) in addition to, or instead of, this proposal.
Languages in the C# family generally prefer punctuation for operators rather than keywords...
Maybe that was the case at one point in time but not any more. There are a plethora of examples in C# and other languages of word operators. I think readability is much more important than trying to follow design principles from a 40 year old language.
@MgSam
There are a plethora of examples in C# and other languages of word operators.
C# has mostly kept to the same conventions as C/C++. LINQ and await
are the big outliers that come to mind and you could argue that await
falls into the same category as yield
. I'm not opposed to isnot
or not
but I'd rather view them in a larger context such as pattern matching. If not
patterns were closer to being on the table I'd happily prefer that over this proposal. But this proposal is also infinitely smaller and easier to implement and does not preclude the possibility of including those operators in the future.
From the syntax suggested above, not
would still require the inside-out evaluation that you dislike about !
. How would you suggest supporting that keyword without that being a problem? Or are you just arguing for the addition of an isnot
operator which can't be used in general purpose negation scenarios?
@DavidArno
My personal opinions:
- if (foo is not Bar) - π π
What do you mean we never agree on anything? π
(:+1:)
typeof
, nameof
, as
, is
, default
, await
, yield
, the query comprehension form of LINQ, possibly with
in the future. Lots of keyword operators. And with is
getting a major upgrade as part of pattern matching, the trend is only being reinforced.
To make C# pattern matching as usable as possible, it creates the immediate need for a better way to express negation in this context. That's why I'd prefer isnot
be focused on rather than simply relaxing the parens requirement on if
, though I'm not opposed to doing both.
My issue isn't about the order of evaluation- it's about how it's mentally parsed when a human being is reading it. I've seen far too many logic errors from people sprinkling !
operators outside of nested expressions and then it being too hard to mentally parse what's going on so they make a mistake somewhere. And that is only like to get even worse with pattern matching only having a positive form of the expression. This proposal, while it does decrease verbosity, does not solve that part of the problem.
It's directly analogous to the !=
operator. No language needs a !=
operator. You could easily express any logic using only the unary !
. But the huge benefits of being able to directly express a negative condition outweigh the cost of extra syntax.
@MgSam
That's why I'd prefer
isnot
be focused on
Well it's not like the team can't do both.
I'd prefer not
combined with the is
operator to keep the syntax consistent regardless of how you use the pattern, e.g. if (foo is not Bar)
or case not Bar:
If it came down to an either-or proposition I would certainly prefer that the team focus on not
.
rather than simply relaxing the parens requirement on
if
This proposal doesn't go that far. Actually it's been demonstrated that removing the parens requirement will create syntax ambiguities. All this proposal does is allow an optional !
outside of the parenthesis in order to negate the entire condition.
My main problem with it is that it still forces negative
is
expressions to be inside out.My issue isn't about the order of evaluation
Well, which is it? :grin:
All this proposal does is allow an optional ! outside of the parenthesis in order to negate the entire condition.
I'd prefer to see it as an if
followed by an optional !
i.e. a modifier on if
: if! (e)
, rather than an operator on (e)
: if !(e)
, which doesn't make sense at all. /s
well if if!(e)
becomes valid (i.e. no space required after if
), you can enforce that with an analyser... π
Sometimes I miss the simplicity of unless
in Perl:
unless (success) { throw new InvalidOperationException("..."); }
@codefox42 #138 is probably the closest we'd get.
In a V1 i think i would prefer "not". However, C# has already shipped 7 (soon to be 8) versions with an existing "not" operator !
. We should use what we have when it makes sense. We don't need to invent synonyms.
@CyrusNajmabadi
Agreed. I'd only consider not
within the context of pattern matching where I believe @alrz demonstrated that reusing !
would cause ambiguities. It wouldn't be usable as a replacement to !
, e.g. not y == x
would not compile. But maybe foo is not Bar
could, as well as case not 0
and, potentially, pt is Point(var x, not 0)
whenever recursive patterns get off of the ground.
@CyrusNajmabadi You and others are probably right about not
, I thought it was a good idea but anyway, what do you guys think about unless
? is adding a new keyword for it off the table? just curious.
@HaloFour As demonstrated in #277 we should actually consider reusing !
but I think its precedence would be an issue, and tbh it's not as readable as not
in a pattern e.g. !0
doesn't make much sense.
@eyalsk I think unless
is relatively more expensive since unless(e);
is a valid invocation expression and we need to reconstruct it as an unless
statement with a semicolon in the body after semantic analysis. As long as if!
works I don't think it would worth it, however I agree it's a little strange compared to other proposed keyword modifiers like foreach?
and await?
.
@alrz I understand, thanks. ;)
@jnm2,
It's not just you agreeing with me; five six eight people do! Not since the best bits of pattern matching were dropped from C# 7 have I found myself being agreed with so much. π
With if
!
seems more natural, but with is
not
seems more natural. If there's both then there's the question why there are 2 ways to do the same thing (code style issues, combination issues). So I would guess ! is
would have to do. I don't like isnot
, it's like elseif
and that's just improper English.
Also, I don't like the removing of parens. It makes for sloppy one-liners. It optimizes for typing but not for reading.
@sirgru
The issue is that pattern matching extends beyond is
. Would we also have !case
? What about with recursive patterns where there wouldn't be an additional keyword? point is Point(var x, !0)
?
@HaloFour,
Is the following proposed for recursive patterns?
point is Point(var x, not 0)
If so, I definitely agree that not
makes a lot more sense here than !
does and it's a lot more compact than
point is Point(var x, var y) when y != 0
ok I'm convinced if we're going to do if!
syntax we could do it for all statements and require blocks to disambiguate.
that said, the example @gafter gave becomes a compile-time error.
if exprDoNotStartWithOpenParen
(M)(e1)(e2); // ERROR: expected `{`, found `;`
then we have no changes in the syntax node, just the parser.
Although, you can't do if !(e) return;
anymore, instead you should write if !(e) { return; }
.
A corner case would be object initializers, if new bool() {}
and if new bool() {} {}
both may become valid syntax, depending on how we handle the initializer block.
@alrz So we have to have either parens or braces in one liners. What are we saving here by omitting the parens? It introduces a new "unexpected" rule.
I think the biggest reason for using not
instead of !
(or allowing both) is that !
can be easy to miss sometimes, especially if it is just at the beginning of a complex expression.
Most operator/punctuation characters are either visible by themselves or tend to have whitespace surrounding them to make them more readily visible (most of them, such as &
, +
, and -
are ~10 pixels wide on my screen, others such as ,
and ;
are ~5px). However, !
is visibly small and is easily swallowed when intermixed with parenthesis and other statements (it is 3px on my monitor, with 2 of those pixels being used for anti-aliasing). -- Note: The pixel sizes are from a 4K screen with 150% scaling (OS scaling, not VS scaling) using the default font in VS.
Allowing if!(expression)
suffers the same problem as what we have today, the !
gets swallowed by the surrounding characters and is easy to miss. if !(expression)
is a bit better (and more readable than today's if (!(expression))
), but it is still not quite as "parsable" (at least by my eyes) as if not (expression)
or if ((expression) == false)
@sirgru
So we have to have either parens or braces in one liners
Note that the case for a negated if statement is not conciseness, it's about readability (as mentioned in the linked proposal).
What are we saving here by omitting the parens?
Rust for instance, did this from day one and nobody ever wanted a "negated if statement". I think that pair of parentheses are the reason we're here. Besides, most of codebases out there are using blocks anyways, because it's more readable and have better diff when modified.
It introduces a new "unexpected" rule.
I think consistency is important, even though reusing !(e)
seems like a good idea, it also is a valid expression and an embedded-statement right next to it, is not what I'd expect. if we just do this for if
it looks like you are ommiting parens from if
, but it's actually a new syntax and you don't have it in other statements. that is also unexpected.
@tannergooding
However, ! is visibly small and is easily swallowed when intermixed with parenthesis and other statements
Same would apply to the post-fix "not null" operator. The fact that it's post-fix may cause it to be totally overlooked.. so, I think we're stuck with it?
I don't think it will be quite as bad.
The biggest issue with !
, today is it tends to not have surrounding whitespace. I think this is why if (!(expression))
is hard to parse but if !(expression)
is so much better (even if not "ideal"). a != b
is likewise, not hard to parse, both due to the surrounding whitespace and due to the height difference between !
and =
.
There will certainly be some types where the not null
operator is hard to read, such as roof!
(which is also why I probably don't like if! (expression)
and if!(expression)
), but it should be a bit more readable Ex: string! value
, Exception! value
, IDisposable! value
, object! value
, etc.
Agree for if !(expression)
And I prefer obj not Type
than obj is not Type
. At least it should be obj isnot Type
@Thaina what about case not Type
? Then since we already replace case
with obj is
, that composes as obj is not Type
.
@jnm2 At least case
is special one that need it own line and special syntax case SomeThingSomeThing :
. is
on the other hand tend to stay with many things around it in if clause so SomeCondition && obj is not Type && SomeOtherCondition
seem a bit hard to read for me
seem a bit hard to read for me
Meh, I used to be pro isnot
or isnt
but I can see it both ways.
I would just replace "!" with "not" because it's much more readable...
[I] think
!
is more readable thannot
...I just think that
not
would be more readable......it's not as readable as...
...to make them more readily visible...
I think the one conclusion we can reach here is that a lot of us could probably do with an eye test! π
I think both if !()
and if not
emphasis on the code that would be executed if the condition is not true. But IMO when you want to write if(!(obj is Foo foo))
the emphasis is on the code that comes after the if statement, you just want to make sure that the condition is true on the rest of the block. Maybe we should also consider guard
statement or something similar here.
So I think these are available options so far.
if!
, if not
or unless
.if (obj is not T t)
.is
operator, e.g. if (obj isnot T t)
.guard
with a block that has an unreachable end.not
with lower precedence, allowing if (not obj is T)
.My preference is if!(...) Also donβt forget while!(...)
I can go either way with βis notβ or βisnotβ. That would depend on which allows the IDE team to provide the best experience.
I accidentally made myself a negated if
statement π
if (condition); else
{
}
This is basically the guard
statement,
if (argument1 is StatementSyntax statement1); else throw new ArgumentException();
if (argument2 is StatementSyntax statement2); else throw new ArgumentException();
Sadly, auto formatter doesn't play nice with it and also you get "possibly mistaken semicolon" warning.
My preference is just
if (expression == false)
without any extra syntax.
@alexzzzz that does not invert the definite assignment state of the expression as !
does.
I'm in favor of unless
and until
. Much more straightforward. I'd even go as far as to say that all the other alternatives are awkward and I'd prefer to live with the status quo.
Now, it's already been stated that they might be harder to implement and that, e.g., unless(e);
is a valid invocation expression. Although technically correct, I wouldn't rule them out because of these arguably rare occurrences. There might be good solutions to these cases. Also, we should think of the usability of the language overall, regardless of the amount of effort to introduce the feature, unless it becomes prohibitive, which I don't believe to be the case here.
Just my 2 cents...
@gafter
that does not invert the definite assignment state of the expression as ! does.
To me it feels much less of a problem than incorrect semantics of !
most of the time and its poor visibility.
To me it feels much less of a problem than incorrect semantics of !
What incorrect semantics does !
have?
and its poor visibility.
!
is very idiomatic C-style language negation. I don't think visibility of it is a serious problem that needs to be addressed
I used to be rather opposed to isnot
or even is not
had it been just an extension of the is
operator but now seeing that not
could actually be a pattern that could also be used with case
and even recursively, that makes a lot of sense to me and it takes away the concern of having a not
keyword that would just be a replacement for !
, because not
as a pattern would be very different and not interchangable - not 5
and not Bar
would be a pattern whereas !Bar
makes not sense at all.
@Neme12
I do agree, however the language already supports !
as a prefix to a pattern, at least in the very specific case of constant bool
patterns:
if (b1 is !false) { ... }
switch (b2) {
case !true: ...
case !false: ...
}
It's more an artifact of the compiler being able to evaluate the Boolean expressions at compile-time so that !false
is really true
, but that might set a precedent. Syntax aside, I think that a not
/!
pattern would be quite useful.
See https://github.com/dotnet/csharplang/issues/568 where this idea was recently introduced.