Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Proposal: inverted null-coalesce operator ?! #15823

Closed
niklaskallander opened this issue Dec 11, 2016 · 36 comments
Closed

Proposal: inverted null-coalesce operator ?! #15823

niklaskallander opened this issue Dec 11, 2016 · 36 comments

Comments

@niklaskallander
Copy link

niklaskallander commented Dec 11, 2016

I'd very much like to see a new operator in C# that kind of does the opposite of the null-coalesce operator ?? (like the null propagation operator ? but still different).

Usage example:

string possibleIntInput = ...

int? possibleIntValue = possibleIntInput ?! int.Parse(possibleIntInput);

If possibleIntInput is null, then null is returned otherwise an attempt is made to parse it (or whatever other thing you'd want to happen as long as the left-hand object/value is not null).

Then you could avoid less expressive code like:

string possibleIntInput = ...

int? possibleIntValue = possibleIntInput != null
    ? int.Parse(possibleIntInput)
    : null;

#5445 proposes forward piping with null propagation behavior, and I would love being able to write something like this (it's so expressive and functional-like):

string possibleIntInput = ...

int? possibleIntValue = possibleIntInput ?> int.Parse;

but I don't just want forward piping, I also want a syntax for either null or {whatever code} (that returns an object/value of whatever type is expected by the context).

What do you think?

@Unknown6656
Copy link

I personally think, that it is a wonderful idea, I do however dislike the token ?!, as it is not intuitive (IMO) and could possibly create some confusion with a ternary operator followed by a logical not: a ? !b : c =/= a ?! b : c.

I would like to see the pipe-operator |> and its null-coalescent partner ?> implemented, as I love functional programming and personally think, that C# could be more powerful in this direction.

@niklaskallander
Copy link
Author

niklaskallander commented Dec 11, 2016

Ah, never thought of that scenario, nice!

What token to use is of no big importance to me though. You may be able to flip it around to !? instead but I'm not convinced you'd find that more intuitive.

My initial thought of ?! was to build on the null propagation syntax but somehow indicate a "hand-over" (or whatever one would want to call it) instead of a continued use of the null-checked object (as in the case of null propagation).

I like ?> but I think that token fits better for a forward piping scenario.

How about ?: ?

@qrli
Copy link

qrli commented Dec 11, 2016

Using existing features:

string possibleIntInput = ...

int? possibleIntValue = possibleIntInput?.ParseInt32();

static int? ParseInt32(this string str) { ... }

@qrli
Copy link

qrli commented Dec 11, 2016

Or with a generic helper function:

static TResult Do<TInput, TResult>(this TInput input, Func<TInput, TResult> func)
{
  return func(input);
}
string possibleIntInput = ...
int? possibleIntValue = possibleIntInput?.Do(int.Parse);

@niklaskallander
Copy link
Author

niklaskallander commented Dec 11, 2016

Yes, it is possible to "overcome" this using extension methods. However, I don't find extension methods as expressive, and, if I were to start passing delegates around I'd have to check them for null too.

It's not ideal!

@iam3yal
Copy link

iam3yal commented Dec 11, 2016

Just a crazy idea but what do you think about the following syntax:

possibleIntInput !! int.Parse(possibleIntInput)

@niklaskallander
Copy link
Author

I'm all for it as long as it works with the rest of C#! (:

@iam3yal
Copy link

iam3yal commented Dec 11, 2016

Yeah it's a bit tricky. 😄

@orthoxerox
Copy link
Contributor

int? possibleIntValue = 
from x in possibleIntInput
select int.Parse(x);

The only problem is, this doesn't work because nullable value and reference types don't mix until/unless we get nullable reference types in C# vNext.

@niklaskallander
Copy link
Author

niklaskallander commented Dec 11, 2016

Also, string is an IEnumerable<char> so I don't think it would work particularly well at all? Am I missing something?

@orthoxerox
Copy link
Contributor

@niklaskallander Yes, but is string? one?

@niklaskallander
Copy link
Author

@orthoxerox Again, am I missing something?

@orthoxerox
Copy link
Contributor

@niklaskallander it's possible to implement Select and SelectMany on nullable types that unwrap the inner value for processing.

@ufcpp
Copy link
Contributor

ufcpp commented Dec 12, 2016

You can do nearly the same with an extension method:

using System;

static class Extensions
{
    public static TResult Apply<T1, TResult>(this T1 x, Func<T1, TResult> f) => f(x);
}

class Program
{
    static int? F(string s) => s?.Apply(int.Parse);
}

@niklaskallander
Copy link
Author

niklaskallander commented Dec 12, 2016

@ufcpp Using extension methods has already been proposed by @qrli a few comments ago, and as I replied to his proposal: Extension methods are not as expressive and passing delegates gives you more stuff to null-check.

It's not ideal!

@orthoxerox Now I follow, thanks for explaining! Unfortunately, as with all other proposals using extension methods (which this also is but with some sugar on top), it's not as expressive and the linq syntax is too verbose for my liking.

@DavidArno
Copy link

It's possible that I'm focusing too much on the example here, rather than the syntax proposal. But the example line:

int? possibleIntValue = possibleIntInput ?! int.Parse(possibleIntInput);

worries me. If possibleIntInput is "hello", an exception will be thrown anyway. Therefore this syntax suggestion will only offer null protection and will offer that protection by propagating that null, rather than dealing with it.

I'd prefer to see the team focusing on pattern matching and unions, so an Option<T> solution could be employed instead:

Option<int> possibleIntValue = int.TryParse(possibleIntInput);

which would yield none or Some<int>.

@niklaskallander
Copy link
Author

niklaskallander commented Dec 12, 2016

@DavidArno Thanks for your comment. The example is not there for demonstrating the problem I want to solve with the proposal, it is there to demonstrate the syntax of the proposal. A better example might be:

MyResultObject result = myRequestObject ?! PerformTasksFor(myRequestObject);

But as @Unknown6656 said a few comments above the token ?! should probably be something else as to not confuse it

with a ternary operator followed by a logical not

With language features for both null-coalescing and null propagation I feel that this one is missing.

(And, I'd also like to see more functional concepts in C# such as discriminated unions, immutable record types, forward piping etc.)

@iam3yal
Copy link

iam3yal commented Dec 12, 2016

@DavidArno Personally, I think that Option<T> is a lot better than null because it makes things explicit but it still has its own problems as described here, finally in the case of int.TryParse(...) the value of None is inevitably null so if the parsing failed and you didn't check for it you will still get NRE.

@niklaskallander
Copy link
Author

@eyalsk The thing with discriminated unions (Option<T>) is that you should be forced by the compiler to check for all outcomes = no NRE.

@iam3yal
Copy link

iam3yal commented Dec 12, 2016

@niklaskallander I understand, I really hope for #5032.

@NetMage
Copy link

NetMage commented Dec 7, 2017

and could possibly create some confusion with a ternary operator followed by a logical not: a ? !b : c =/= a ?! b : c.

I don't see how that could create confusion: a ?! b : c is a syntax error. What is the : hanging out for?

@CyrusNajmabadi
Copy link
Member

a ?! b : c is a syntax error.

No it isn't. It's totally legal syntax today. It would be interpretted as:

a ? (!b) : c

@NetMage
Copy link

NetMage commented Dec 8, 2017

I was thinking if the new operator was valid. Unfortunately it would break existing code like your example.

@iam3yal
Copy link

iam3yal commented Dec 9, 2017

@niklaskallander Proposed new language features should be discussed at the C# repo so you may want to close this and open a new issue there.

@v-kydela
Copy link

If we're going for syntactic sugar then piping would surely be ideal because the reverse-coalescing would still require you to provide the input twice

@drdamour
Copy link

drdamour commented Mar 5, 2020

I often have the pattern of
identity.HasPermissionX ? new X(...) : null and new X(...) is usually some multiline db query linq statement and that :null get's thrown at the end.

with this i could change it to
identity.first(p > p.name == 'can do x') ?! new X(...)

so it'd be cool if the left hand side could treat false as nully (or null as falsey)

@breket
Copy link

breket commented Mar 25, 2020

I need this feature. But my proposition about alias is !?? like opposite to ??
car.vendor = vendorId !?? getVendorById(vendorId)

This means: when vendorId is Null then assign Null to car.vendor, otherwise get vendor by that vendorId.

You can also write the same using
car.vendor = vendorId == null? null: getVendorById(vendorId)
but my first examlpe looks better =)

@CyrusNajmabadi
Copy link
Member

!?? is already legal syntax in use today

@breket
Copy link

breket commented Mar 25, 2020

!?? is already legal syntax in use today

and what does it do? My VS does not recognize it

@CyrusNajmabadi
Copy link
Member

and what does it do? My VS does not recognize it

x!??y would suppress NRT annotations on 'x' (!), and then perform the coalescing operation (??).

@ChristopherHaws
Copy link

ChristopherHaws commented Jun 26, 2020

This is possible in JavaScript using the && operator. Would love to see something similar added to C# :)

console.log(null && "Foo") // null
console.log({} && "Bar")   // Bar

I would say ?: makes the most sense (since if your remove the null's and whitespace from foo is null ? null : "foo" that is what is left) but it already has a different meaning in many other languages which might be confusing.

@iricigor
Copy link

iricigor commented Feb 11, 2021

Bumping this up.

Here is one use case example (presuming !? is new operator):

return FirstName + (MiddleName !? $" {MiddleName}") + ' ' + LastName;

@drdamour
Copy link

Bumping this up.

Here is one use case example (presuming !? is new operator):

return FirstName + (MiddleName !? $" {MiddleName}") + ' ' + LastName;

Isnt
‘’’
MiddleName ?? “”
‘’’

effectively the same there?

@v-kydela
Copy link

@drdamour - Try it. You may notice there's an extra space when there's no middle name.

@mrwensveen
Copy link

mrwensveen commented Feb 11, 2021

Bumping this up.
Here is one use case example (presuming !? is new operator):

return FirstName + (MiddleName !? $" {MiddleName}") + ' ' + LastName;

Isnt
‘’’
MiddleName ?? “”
‘’’

effectively the same there?

This is getting a little of topic. But, no, that's not the same because of the space @iricigor wants to insert a space before MiddleName if it isn't null. His example isn't the best example because there a thousand other ways to solve that problem, but that's not the point.

PS. Here's one way: return string.Join(" ", new [] { FirstName, MiddleName, LastName}.Where(s => !string.IsNullOrEmpty(s)));

@drdamour
Copy link

@drdamour - Try it. You may notice there's an extra space when there's no middle name.

Ah yeah, missed the space. Nice example

@dotnet dotnet locked and limited conversation to collaborators Feb 11, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Projects
None yet
Development

No branches or pull requests