-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Lifting support for nullable types (and other LINQy entities). #8022
Comments
I've just realized that
Did I want to say I see two possible solutions:
|
What is the problem with this? int? a = null;
var b = a * 2; // b will be null if a is null
struct Foo { public int A; }
Foo? foo = null;
var a = foo?.A; // will compile, a will be null if foo is null
object? a = null;
var b = a.GetType(); // error/warning depending on #5032
object? a = null;
var b = a?.GetType(); // OK |
@alrz the nullable object is the dispatcher in all your examples.
In addition to that it will simplify LINQ queries (but see my comment about reusing IEnumerable<Foo> foos = GetFoos();
//var xs = foos.Select(foo => foo.X);
var xs = foos?.X; |
You are clearly passing a nullable
And what it has to do with LINQ? |
@alrz Am I allowed to pass a nullable Enumerables, Nullables, Tasks are all monads. Right now they all have different ways to simplify working with them. Nullables have lifted operators (even though From-notation is the only extensible way to handle monadic entities, similar to However, from-notation forces you to introduce additional variables to handle them. For example, to zip two enumerables you either have to use a from x in xs
from y in ys
select MyFunc(x, y) With a lifted |
I think not, that involves an implicit unwrapping which totally misses the point of nullable reference, however, as far as I know the following is OK, Foo? b = null;
if(b != null ) a.Foop(b); // b is definitely not null which doesn't throw in the method. The syntax you are using is similar to what I've been proposed for #5961 which as discussed wouldn't be really practical (see this comment). As for, IEnumerable<Foo> foos = GetFoos();
//var xs = foos.Select(foo => foo.X);
var xs = foos?.X; I don't think for C# that would be really good style of coding, I'd rather use #5444 to make it more concise like var result = from item in enumerable select item.X;
var result = (await task).X;
var result = nullable?.X; Just as you said, Enumerables, Nullables and Tasks are monads, but all of them have their own syntax, and we don't have any abstraction over these types. With traits and higher kinded generics you might be able to declare an abstraction over them, but there is no general syntactic sugar in C# like |
@alrz Hah, I actually forgot what your proposal was about. Perhaps it's too late for C# to unify its approach to monads beyond from-notation. Or perhaps not. |
Looks like someone wrote a Scala macro with a very similar syntax: https://github.com/pelotom/effectful |
See also #5961 |
I must say, writing a functional LINQ expression to zip and sum three |
I've been jotting down an expression-rewriting algorithm to implement this and decided to share with you the madness that mixing different monads creates. var x = new [] {1, 2, 3};
var y = new Option<int>(5);
var w = (Func<int>)(() => 100);
var z = x! + y! * w!;
//is ultimately transformed into:
var z = y.Select(y_ => w.Select(w_ => y_ + w_)).Select(yw_ => x.Select(x_ => yw_.Select(yw__ => x_ + yw__)));
//I have no idea how to even format this monstrosity! Unfortunately, the type of var x = new Option<int>(5); //Option<int>
var y = new [] {Some(1), Some(2), Some(3)}; //IEnumerable<Option<int>>
var z = x! + y!!; //should be //IEnumerable<Option<int>>
//is correctly transformed into:
var z = y.Select(y_ => x.Zip(y_, (x_, y__) => x_ + y__)); |
We are now taking language feature discussion on https://github.com/dotnet/csharplang for C# specific issues, https://github.com/dotnet/vblang for VB-specific features, and https://github.com/dotnet/csharplang for features that affect both languages. |
Right now C# supports implicit lifting for operators on nullable value types like
int?
:However, this doesn't work on other methods:
With explicitly nullable reference types coming to C# in #5032, this gets a bit problematic. Lifting all functions implicitly would be a huge problem, since it would silently return
null
where you would get aNRE
before:I want to propose explicit lifting using
?
as a postfix operator. For example,var result = func(foo?, bar?);
from my previous example would be rewritten as:I also propose to generalize the lifting operator to support all types that support LINQ functions. For example, the same
var result = func(foo?, bar?);
could be rewritten asOr:
Or the perverse examples:
The text was updated successfully, but these errors were encountered: