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

&&= and ||= #397

Closed
TonyValenti opened this issue Apr 1, 2017 · 46 comments
Closed

&&= and ||= #397

TonyValenti opened this issue Apr 1, 2017 · 46 comments

Comments

@TonyValenti
Copy link

Hi All,
I would like to request that C# add &&= and ||= operators so I can write code as follows:

var ret = false;
ret ||= TryMethod1();
ret ||= TryMethod2();
ret ||= TryMethod3();

@AdrienTorris
Copy link

What behavior do you want for the operators &&= and ||= ?
Your code is just like :
var ret = TryMethod3();

@Pzixel
Copy link

Pzixel commented Apr 1, 2017

@TonyValenti what's wrong with

var ret = TryMethod1()
|| TryMethod2()
|| TryMethod3();

?

And of course you know, && and || are lazy operators and they halt when result cannot be changed due to absorption with true/false constant.

You can write

var ret = false;
ret |= TryMethod1();
ret |= TryMethod2();
ret |= TryMethod3();

It's completely valid in C#. But your proposition just doesn't make sense.

@miloush
Copy link

miloush commented Apr 1, 2017

@AdrienTorris it's not, if any of the previous method returns true, I assume that TryMethod3() would not be called.

@drewnoakes
Copy link
Member

drewnoakes commented Apr 1, 2017


Edit: My mistake.

@miloush
Copy link

miloush commented Apr 1, 2017

@drewnoakes I might expect that from something like ??= but not &&= or ||=

@drewnoakes
Copy link
Member

Derp. You're right. That's what commenting from a sofa looks like :)

I was wondering about ??= the other day, then saw this. Brian conflated the two.

@TonyValenti
Copy link
Author

TonyValenti commented Apr 1, 2017 via email

@jnm2
Copy link
Contributor

jnm2 commented Apr 1, 2017

??= is by far the most common thing I do (return x ?? (x = CalcX())) but it just feels wrong not to have &&= and ||= as well.

@CyrusNajmabadi
Copy link
Member

I would be willing to champion this. But i haven't had time to work on my other championed features yet :-/

@drewnoakes
Copy link
Member

Existing issue #34 tracks support for null-coalescing assignments via ??=.

@DavidArno
Copy link

I'm confused by this request (especially by the suggestion that @CyrusNajmabadi would consider championing it), as I don't see how it would achieve anything that isn't already offered by the language.

As @Pzixel points out, the OP can achieve the requested functionality in his example, using:

var ret = false;
ret |= TryMethod1();
ret |= TryMethod2();
ret |= TryMethod3();

So what would the ||= operator bring to the language?

@yaakov-h
Copy link
Member

yaakov-h commented Apr 2, 2017

It wouldn't always evaluate the right-hand side.

@DavidArno
Copy link

DavidArno commented Apr 2, 2017

@yaakov-h,

Ah, I get you. Again as @Pzixel suggested, this can already be achieved via:

var ret = TryMethod1()
    || TryMethod2()
    || TryMethod3();

And if the two tests need to occur with some other statement between them, then the two results have different meanings, so should have different variable names:

var result1 = TryMethod1();
DoSomething();
var result2 = result1 || TryMethod2();

But I know some folk like to overload their variables with multiple meanings, so I can see why ||= would appeal to those folk.

@yaakov-h
Copy link
Member

yaakov-h commented Apr 2, 2017

I agree that it can already but achieved, however I also see some value in breaking the intermediate result out into a variable.

This allows for easier debugging, and naming of the variable can aid in readability and intrinsic documentation.

@DavidArno
Copy link

This allows for easier debugging, and naming of the variable can aid in readability and intrinsic documentation.

Indeed, which is why it's a good idea to avoid using +=, -=, |= etc and to instead assign the result to a new variable, for "naming of the variable can aid in readability and intrinsic documentation."

@jnm2
Copy link
Contributor

jnm2 commented Apr 2, 2017

@DavidArno This works especially well with if statements. I treat mutable variables like this as builders. The meaning doesn't change during construction.

@CyrusNajmabadi
Copy link
Member

CyrusNajmabadi commented Apr 2, 2017

&&= and ||= are good for general accumulation-style algorithms (just like += and the like). ??= is good for algorithms that want to lazy initialize something only when necessary. i.e.:

// making a connection is expensive and should be deferred until necessary.  once made, the
// same connection should be used from that point on.
SqlConnection connection = null; 

foreach (var blah in whatever)
{
    // lots of logic
    ...
    if (yupINeedConnection)
    {
        connection = connection == null ? new SqlConnection() : connection; // or
        connection = connection ?? new SqlConnection();
        // use connection.
        ...
    }
}

This is more nicely written as:

// making a connection is expensive and should be deferred until necessary.  once made, the
// same connection should be used from that point on.
SqlConnection connection = null; 

foreach (var blah in whatever)
{
    // lots of logic
    ...
    if (yupINeedConnection)
    {
        connection ??= new SqlConnection();
        // use connection.
        ...
    }
}

&&= and ||= are generally useful while looping, and while you need to process every element, but where you want to accumulate some final boolean value. For example:

var runAgain = null;

foreach (var token in this.DescendentTokens())
{
     // do a lot of stuff.

     runAgain = runAgain || ComputeSomethingExpensive(something);
}

This is nicer as:

var runAgain = null;

foreach (var token in this.DescendentTokens())
{
     // do a lot of stuff.

     runAgain ||= ComputeSomethingExpensive(something);
}

Basically, the operators just simplify general lazy/accumulation patterns that do show up elsewhere.

Is this super important? Definitely not. But it's nice and consistent, and likely easy to slot in.

Basically, any time the language has an operator that you could do: "x = x op y", then it would be nice to have a "x op= y" version for uniformity.

@HaloFour
Copy link
Contributor

HaloFour commented Apr 2, 2017

I can't find it at the moment but I recall &&= and ||= coming up during another conversation and Mads commenting that he didn't like the idea. Of course things always change...

@jnm2
Copy link
Contributor

jnm2 commented Apr 2, 2017

@CyrusNajmabadi With the loop, wouldn't you prefer to break rather than redo the same truth test for the rest of the loop?

@CyrusNajmabadi
Copy link
Member

CyrusNajmabadi commented Apr 2, 2017

@jnm2 Only if the goal was to determine if we needed to set "runAgain". But often you may still have other work you need to do with the rest of the items in your collection.

Hence the " // do a lot of stuff." line. The implication here is that it's important that we process all the items. But that we only want to ComputeSomethingExpensive as long as it is returning false. once it returns 'true' once, we don't need to again.

@DavidArno
Copy link

@CyrusNajmabadi,

Your example highlights different styles of programming creating different syntax needs. Your example:

foreach (var token in this.DescendentTokens())
{
     // do a lot of stuff.

     runAgain ||= ComputeSomethingExpensive(something);
}

would end up something like the following in my hands:

foreach (var token in this.DescendentTokens())
{
    DoALotOfStuffWithToken(token);

    if (expensiveComputationProvider.RunAgain)
    {
        ...
    }
}

I'd expect the state of whether runAgain is true to be abstracted away to both aid testing and to keep the method focused on its job, rather than having to manage state too.

However, I like the idea of ??=, as that would tidy up lazy properties:

bool RunAgain => (_expensiveComputationResult ??= ComputeSomethingExpensive()).RunAgain;

And if ??= were added, then the lack of ||= and &&= become a strange inconsistency in the language syntax.

But seriously, could we please complete pattern matching and add match, records and DU's to the language before we start doing the little "nice to have" stuff like this? Pretty please?

@orthoxerox
Copy link

@DavidArno this little nice to have stuff could probably be done by the community, if the LDT ever decides to relax their iron grip on the development reins.

@CyrusNajmabadi
Copy link
Member

I'd expect the state of whether runAgain is true to be abstracted away to both aid testing and to keep the method focused on its job, rather than having to manage state too.

Managing algorithmic state is totally fine for a function to do IMO :)

I mean, do you not accumulate values in your functions ever? Do you never use ++ anywhere? After all, you could take that incremented local state and put it in some other "computation-provider" struct/class.

Regardless, people have state in functions today. All the time. I see nothing wrong with making working with that state more pleasant.

--

But seriously, could we please complete pattern matching and add match, records and DU's to the language before we start doing the little "nice to have" stuff like this? Pretty please?

There is no way that i'll be able to champion any of those things to hte language. I do not have the time, and i'm completely booked on working in the IDE space and on things like IOperation. But, i can potentially squeeze in time for language features that i think just make things more pleasant at low cost. Hence why i'd be willing to champion it. If i don't champion it, it just means that it doesn't get done. It doesn't mean those other things get done.

@CyrusNajmabadi
Copy link
Member

@orthoxerox At the end of the day, the LDM is going to decide what does/doesn't go into the language. Just consider how many proposals are out there. Consider how many of them you probably don't like :) Now consider what sort of language you have if there isn't something preventing all those language changes from just getting added to the language whenever someone in the community wants it. :)

@DavidArno
Copy link

DavidArno commented Apr 3, 2017

@CyrusNajmabadi

I mean, do you not accumulate values in your functions ever? Do you never use ++ anywhere?

I'd be lying if I said I never used such features. But it is incredibly rare that I do. Different approaches...

@DavidArno
Copy link

DavidArno commented Apr 4, 2017

@CyrusNajmabadi

At the end of the day, the LDM is going to decide what does/doesn't go into the language. Just consider how many proposals are out there. Consider how many of them you probably don't like :) Now consider what sort of language you have if there isn't something preventing all those language changes from just getting added to the language whenever someone in the community wants it. :)

Exhibit A: out/is var and scope leakage.

Near-universally disliked by the community, but we got it anyway. Clearly the LDM team doesn't protect us from features we don't like...

@jnm2
Copy link
Contributor

jnm2 commented Apr 4, 2017

GitHub is not a large percentage of the entire community from what I gather.

@DavidArno
Copy link

DavidArno commented Apr 4, 2017

@jnm2,

Ah yes, I forgot that significant part of the "community" that communicate with the LDM in secret... 😆

@jnm2
Copy link
Contributor

jnm2 commented Apr 4, 2017

@DavidArno Who says the communication is secret?

@orthoxerox
Copy link

@DavidArno Yes, you forgot about enterprise customers that ask for features through their account managers.

@DavidArno
Copy link

@jnm2,

That communication takes place (such communications were used as a major argument for out var scope leakage, for example), yet they aren't public. So they are secret.

@orthoxerox,

And that, I feel, is the real reason behind the LDT keeps a vice-like grip on new features. Those enterprise customers would be less than happy if they suddenly had to argue their case with the community.

@gafter
Copy link
Member

gafter commented Apr 4, 2017

This feature request is already an open question as part of an existing championed proposal, which has been triaged by the LDM for possible inclusion in some minor or major release.

@drewnoakes
Copy link
Member

@gafter, I may be mistaken but I think this issue differs. It's referring to logical &&= and ||=, not null coalescing assignment via ??=.

@GeirGrusom
Copy link

GeirGrusom commented Apr 5, 2017

@drewnoakes It's almost the same thing:

a &&= b = a = a && b
a ||= b = a = a || b
a ??= b = a = a ?? b

@drewnoakes
Copy link
Member

@GeirGrusom, they're similar, but not the same.

There's a separate issue tracking ??=: #34

@GeirGrusom
Copy link

How does this issue differ really? The difference between ??= and &&= is as big as the difference between ||= and &&=. They only differ in what value they compare against.

@gafter
Copy link
Member

gafter commented Apr 10, 2017

@gafter wrote:

This feature request is already an open question as part of an existing championed proposal, which has been triaged by the LDM for possible inclusion in some minor or major release.

@drewnoakes wrote

@gafter, I may be mistaken but I think this issue differs. It's referring to logical &&= and ||=, not null coalescing assignment via ??=.

Quoting from https://github.com/dotnet/csharplang/blob/master/proposals/null-coalecing-assignment.md :

Unresolved questions

  • Requires LDM review
  • Should we also support &&= and ||= operators?

Does that look different to you?

@drewnoakes
Copy link
Member

@gafter perhaps you should close this as a duplicate in that case.

@gafter
Copy link
Member

gafter commented Apr 10, 2017

@drewnoakes This is a discussion issue. The other is a tracking issue for a related championed proposal that may come to include this feature. I don't expect we'll be closing discussion issues.

@Perksey
Copy link
Member

Perksey commented Jul 7, 2020

Wow it's been a hot minute since this has been brought up again but seeing as the null coaelescing operator proposal is pretty much done without these operators included, will this proposal ever be brought back up in an LDM or will it continue to live inconclusively ever after?

@CyrusNajmabadi
Copy link
Member

will this proposal ever be brought back up in an LDM or will it continue to live inconclusively ever after?

It may be brought up in the future if any LDM members wishes to champion it.

@333fred
Copy link
Member

333fred commented Jul 7, 2020

It was championed, and explicitly rejected by LDM: #1718

@Perksey
Copy link
Member

Perksey commented Jul 7, 2020 via email

@333fred
Copy link
Member

333fred commented Jul 7, 2020

I'll leave the championed issue open so that it appears when people search, but I'm going to close this one.

@333fred 333fred closed this as completed Jul 7, 2020
@Perksey
Copy link
Member

Perksey commented Jul 7, 2020 via email

@UdderlyEvelyn
Copy link

UdderlyEvelyn commented Jan 17, 2022

__result is a bool coming in by ref that needs to be changed but also consider its original value in the end value.. we ended up having to write this:

__result = __result && SomeMethod();

Would have been nice to write:

__result &&= SomeMethod();

I realize this is niche, but it.. is a legitimate use case! Heh.

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