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

Please add syntax is let v as a non null equivalence of is var #1192

Closed
Thaina opened this issue Dec 16, 2017 · 36 comments
Closed

Please add syntax is let v as a non null equivalence of is var #1192

Thaina opened this issue Dec 16, 2017 · 36 comments

Comments

@Thaina
Copy link

Thaina commented Dec 16, 2017

I just know that the new syntax obj is var v will always true for null value and it already there in C# 7

So I would like to propose that we should have some syntax for object extraction that also return false for null. So it skip the if clause when it null

Maybe

if(obj?.ToString() is let x)
    Console.WriteLine(x); // x won't be null

or maybe

if(obj?.ToString() is var! x)
    Console.WriteLine(x); // x won't be null

This should also work with nullable struct

And x will be null or default if is is false

@Thaina Thaina changed the title Please add syntax is let v as a non null equivalence of var Please add syntax is let v as a non null equivalence of is var Dec 16, 2017
@Joe4evr
Copy link
Contributor

Joe4evr commented Dec 16, 2017

Related: #306 and #792.

@DavidArno
Copy link

As per @Joe4evr's links, we are pretty much guaranteed to get:

if(obj?.ToString() is {} x)
    Console.WriteLine(x); // x won't be null

That's good enough for me. No need for is let or is var!.

@MkazemAkhgary
Copy link

maybe if you guys leave aside pattern matching for a moment?

if(obj != null) Console.WriteLine(obj.ToString());

@Thaina
Copy link
Author

Thaina commented Dec 17, 2017

@MkazemAkhgary This syntax is for caching and shortening when we have long and deep method call

if(someType.SomeField.SomeProperty.SomeMethod() is let x)
{
    // x here and not null
}

else we need to

var x = someType.SomeField.SomeProperty.SomeMethod();
if(x != null)
{
}

@MkazemAkhgary
Copy link

MkazemAkhgary commented Dec 17, 2017

var x = someType.SomeField.SomeProperty.SomeMethod();
if(x != null)
{
   // x here and not null
}

convince me: does it worth introducing a new syntax for it?

if your syntax is already long, how come removing extra line helps you in the way you read it?

just everything is not pattern matching. one big mistake by language designers is allowing is var x

Its followed by funny mistake other developers make, struggling how to use it.

answer is: just leave it alone. imagine it doesn't exist.


edit: fixed some typos.

@Thaina
Copy link
Author

Thaina commented Dec 17, 2017

@MkazemAkhgary is var x is the already alive syntax

let or var? is not a new syntax

I just inject the already exist syntax into another already exist syntax. It not really new syntax

If you want to argue like that, then that is the argument against is var x in general. Why it was introduced in C# 7?

@MkazemAkhgary
Copy link

Why it was introduced in C# 7?

Ha! I don't know. I was surprised to see it actually is a valid syntax. I am against it since I saw that.

but they are not going to change it, and I think best thing to do is to stop making it worse.

@Thaina
Copy link
Author

Thaina commented Dec 17, 2017

@MkazemAkhgary I see you just against is var x from the start

But for me that syntax is really came to fix out method

I think out method is the real design flawed. And pattern matching come to fix it

The goal is to have a scope that we don't need to make 2 lines to just check if it exist. If you ask does it worth introducing a new syntax for it then I can only answer yes. That is the reason we introduce that syntax from the start

The only flaw is is var x is not work as I expect

@DarthVella
Copy link

var x = someType.SomeField.SomeProperty.SomeMethod();
if(x != null)
{
}

Just a nitpick, but that can be shortened a little to the following:

if(someType.SomeField.SomeProperty.SomeMethod() is var x && x != null)
{
}

@Joe4evr
Copy link
Contributor

Joe4evr commented Dec 17, 2017

And when property patterns are implemented, it can just be

if (someType.SomeField.SomeProperty.SomeMethod() is {} x)

@DavidArno
Copy link

DavidArno commented Dec 18, 2017

someType.SomeField.SomeProperty.SomeMethod()

Law of Demeter. Just saying...

(Edited to remove my unfortunate typo; after giving @jnm2 time to laugh at it 😀)

@jnm2
Copy link
Contributor

jnm2 commented Dec 18, 2017

@DavidArno

Law of Dementer.

Software design meets Harry Potter 😂

@DavidArno
Copy link

Oh my. 🤣 🤣

@Thaina
Copy link
Author

Thaina commented Dec 23, 2017

@Joe4evr @DavidArno I just realize something and got curious

You said that we could use obj is {} x in replace of obj is var x

But will the member variable x still derived from the original object?

I mean if I write code like this

string s = "This is string";

if(s is {} x)
{
    foreach(var a in x.Split(" "))
        Console.WriteLine(a);
}

Won't it get error? Will it suggest the intellisense for string normally?

@MkazemAkhgary
Copy link

But will the member variable x still derived from the original object?

not sure what you mean but {} works like var, it will be automatically inferred from original type. it does not evaluate to derived type. it will be the actual type, in your case, string.

@vladd
Copy link

vladd commented Dec 25, 2017

Isn't is {} x just ugly and counter-intuitive? I'd expect something which reads more naturally.

@orthoxerox
Copy link

orthoxerox commented Dec 25, 2017

@vladd That's because is {} x is a degenerate property pattern. if (x != null) will work well enough to strip the nullability marker from x. Or you can say x switch { null => ..., _ => ... } to (hopefully) achieve the same thing if you need pattern matching.

@Thaina
Copy link
Author

Thaina commented Dec 25, 2017

@vladd It's because they was decide to let the precious is var x be true for null and {} is the last thing we could allowed to have

@gafter
Copy link
Member

gafter commented Dec 27, 2017

When we add support for recursive patterns, we will have the pattern syntax like { X: 3, Y: 4 }. This pattern checks that the input value is non-null, and that its X property is 3, and its Y property is 4. You can use it in an is expression, like if (e is { X: 3, Y: 4 }) or in a switch statement like case { X: 3, Y: 4 }:. You can also give a name to the thing that was matched, for example if (e is { X: 3, Y: 4 } point) assigns the input value to the variable point when it matches.

Of course, you don't have to test exactly two properties. You can test only one, like if (e is { X: 3 } point) which checks that the input is non-null and its X property has value 3.

In fact, you don't have to test even one property. You can test none, like if (e is {} point) which checks that the input is non-null and (when successful) assigns it to the variable point.

Because this way of testing for non-null comes "for free" with recursive patterns, I don't think there would be any point in adding yet another syntax that does the same thing. I am therefore closing this feature request. However, you are welcome to continue discussion here.

@gafter gafter closed this as completed Dec 27, 2017
@Thaina
Copy link
Author

Thaina commented Dec 27, 2017

@gafter Just curious that does this feature work on tuple?

@gafter
Copy link
Member

gafter commented Dec 27, 2017

@Thaina I’m not sure I understand what you are asking.

@Thaina
Copy link
Author

Thaina commented Dec 27, 2017

@gafter

var obj = (X : 4,Y : 2);
if(obj is {X : 4,Y : 2} points) // true or false?
bool Func(object obj)
{
    return obj is {X : 4,Y : 2};
}

Func((X : 4,Y : 2)); // true or false?

@gafter
Copy link
Member

gafter commented Dec 27, 2017

Assuming you add new on the first line...

The first is true. The second is a compile-time error as the type object, which is the static type of the left-hand-side, does not have properties named X and Y.

@Thaina
Copy link
Author

Thaina commented Dec 27, 2017

Assuming you add new on the first line...

What does that means? Did you mean changing tuple to anonymous object?

@sharwell
Copy link
Member

sharwell commented Dec 27, 2017

@gafter There was a syntax issue in the comment from @Thaina. I believe the following is the intended question:

var obj = (X: 4, Y: 2);
if(obj is {X : 4,Y : 2} points) // true or false?

@gafter
Copy link
Member

gafter commented Dec 27, 2017

Ok, then the first will be true. The second is an error, but it could be written this way:

return obj is (4, 2);

@Thaina
Copy link
Author

Thaina commented Dec 27, 2017

@gafter Thank you, then what about

bool Func(object obj)
{
    if(obj is {X is int,Y is int} point)
        return true;
    return false;
}

Func((X : 4,Y : 2)); // true or false?

@HaloFour
Copy link
Contributor

HaloFour commented Dec 27, 2017

@Thaina

That wouldn't compile. The object type has no properties X or Y. You'd have to supply a type pattern in order to test the boxed value against a specific type. But that wouldn't really work with tuples anyway as their names are strictly metadata and lost when they are boxed. At best you might be able to use a positional pattern:

bool Func(object obj)
{
    if (obj is (int, int)) {
        return true;
    }
    return false;
}

Update: Actually the positional pattern might have the same requirement in that you'd have to provide a type against which to match if it is not the same as the type of the expression. I'm not sure how you'd match a boxed tuple in the current pattern matching proposal.

@vladd
Copy link

vladd commented Dec 28, 2017

@orthoxerox I understand this, but from my point of view it's not a clever feature, but rather an abuse of a degenerate case of pattern matching. For such a common use case like null checking I'd assume we need a more expressive syntax.

If you ask any programmer from any language, "what does if (x != null) mean?", he'll give the correct answer even if he did never see a single line in C# before. If you ask him what could be the meaning of if (x is int n), he would probably make a correct guess. But if (x is {} y) is totally incomprehensible for any reader except those who keep in mind the esoteric knowledge that "not null in C# is denoted by empty statement block, don't remember exactly why, there was some funny explanation but I forgot".

We need a separate, human-readable syntax for non-null pattern.

@DavidArno
Copy link

@vladd,

If you ask any programmer from any language, "what does if (x != null) mean?", he'll give the correct answer...

I disagree. != is utterly unintuitive to anyone unfamiliar with C-style languages. Those languages are ubiquitous these days, so most devs will indeed know what it means, but only through learning it.

Likewise, folk will have to learn what is {} means. To my mind, is let is no easier to deduce than is {}. Adding a second way expressing a non-null pattern will make things worse; not better.

@theunrepentantgeek
Copy link

theunrepentantgeek commented Dec 28, 2017

Agreed. Even though it was a very long time ago, I still recall my confusion seeing != for the first time, being used to <> from Sinclair/Amstrad/Applesoft Basic, Turbo Pascal and (I think) LOGO.

No syntax is intuitive, merely familiar. All syntax has to be learned.

@vladd
Copy link

vladd commented Dec 28, 2017

@DavidArno Well, good, let's not play formal games.

Just take any modern reasonable programmer. The percentage of programmers who don't know basic C-style syntax nowadays is 0 with a very high accuracy.

Please don't forget that C#'s syntax decisions (e. g., int x; as opposite to var x: int) are based on the desire to make the language intuitively understandable to the majority of the programmers—that is, to the programmers who know C-style languages. (That's why the language is called C#, starting with C.)

Beside that, adding a really esoteric syntax for a basic feature for just being cool is not going to help C# to become a better language.

@theunrepentantgeek Counterexample to your claim: the syntax for simple pattern matching (if (o is int n)), albeit being unfamiliar, is really clear and intuitive. One doesn't need to learn it and doesn't need to write it down in order to memorize, one can just start using it. On the contrary, empty brace block needs be memorized: there is no mental connection between checking for null and property pattern.

@Thaina
Copy link
Author

Thaina commented Dec 29, 2017

I see the point of @vladd I think the actual downside of is {} is because {} never mean "any object type" before in C#

Worse than that is it similar to empty array declaration

@gafter
Copy link
Member

gafter commented Dec 29, 2017

I see the point of @vladd I think the actual downside of is {} is because {} never mean "any object type" before in C#

It still does not mean that. You can write a type before the braces to indicate the type if you want to test for the presence of an object of some type other then the input type, e.g. if (o is string { Length: 1 } s) tests for a string with a length of 1.

Think of the property pattern (the curly braces and the things within them) more as the pattern-matching analog of an object initializer. An object initializer doesn't require a type (e.g. an anonymous type), but rather it identifies the properties being initialized. In a pattern, the curly braces surround the properties being matched. The mere presence of the braces indicates than an object of some type is expected (i.e. not null) and between the curly braces you identify particular properties or fields of interest to be matched.

@yaakov-h
Copy link
Member

It appears to me to be the pattern matching equivalent of:

public class C
{
    public void M()
    {
        var a = new { };
    }
}

which is already valid today.

@theunrepentantgeek
Copy link

@vladd I suspect you misinterpret my comment; apologies for being unclear.

All syntax needs to be learned.

Learning != wasn't difficult and didn't take more than a few minutes, but it did need to be learnt.

Equally, the syntax if (o is int n) was fast & easy to learn because it was an extension of the already familiar if (o is int) syntax.

Every new piece of syntax will need to be learnt. All of it. This doesn't mean that it will all be hard to pick up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests