csharplang icon indicating copy to clipboard operation
csharplang copied to clipboard

Champion "pattern-based `with` expressions"

Open gafter opened this issue 8 years ago • 14 comments

  • [ ] Proposal added
  • [ ] Discussed in LDM
  • [ ] Decision in LDM
  • [ ] Finalized (done, rejected, inactive)
  • [ ] Spec'ed

In C# 9.0, we did with on records only, not some generalized pattern-based with expression.

See also

  • the proposal in proposals/records.md for one possible approach.
  • Discussion of nested use cases in https://github.com/dotnet/csharplang/issues/77

Design meetings

https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-09-26.md#construction-improvements

gafter avatar Feb 22 '17 00:02 gafter

Why is it tentatively planned for 7.2 if records themselves are in 8.0?

orthoxerox avatar Apr 07 '17 21:04 orthoxerox

Records build on this, not vice versa.

@MadsTorgersen are you working on a proposal for this?

gafter avatar Apr 08 '17 04:04 gafter

pattern-based

I assume it's not only for records (where we need a new instance). For regular objects I think this should also work:

e with { id = v }

->

(var _t = e; _t.id = v; _t)

We have the same situation as switch expression where we possibly want to use with as an statement.

alrz avatar Nov 28 '18 08:11 alrz

@alrz,

Not sure if I'm misunderstanding your example, but if e is a class instance, then surely your wither will modify the original instance, rather than creating a modified copy?

DavidArno avatar Nov 28 '18 09:11 DavidArno

@DavidArno

Yes, I think in the absence of a With method we could just modify the original instance, see https://github.com/dotnet/csharplang/issues/1879 for some examples where this is useful. In that case, with returns the same object on the left-hand-side, so you could return it or use it an statement.

db.Persons.Find(id) with { Name = data.Name, ... };

or simply

db.Persons.Find(id) with { data.Name, ... };

I think there is a need for null checked withers here which could be implemented via with? along with await? and foreach?.

alrz avatar Nov 28 '18 10:11 alrz

@alrz this will change with from a purely syntactic transformation like LINQ query expressions to a more involved one.

orthoxerox avatar Nov 28 '18 10:11 orthoxerox

@alrz, I'd see update blocks as unrelated to this. Having a with expression modify the original data would be really bad in my view.

DavidArno avatar Nov 28 '18 10:11 DavidArno

I think like @DavidArno We should stay with the immutable state of the with keyword.

About the with? suggestion, it looks interesting but I think it is not that useful if we have the strict null-check feature in C#8.

Odonno avatar Nov 28 '18 10:11 Odonno

I agree with @DavidArno It would be confusing if sometimes with changes the original object, and sometimes returns a new one. It would be useful to have two different syntaxes.

YairHalberstadt avatar Nov 28 '18 10:11 YairHalberstadt

@orthoxerox

this will change with from a purely syntactic transformation like LINQ query expressions to a more involved one.

Actually the pattern-based with is not that simple since we first need caller-receiver default-argument (mentioned in the records proposal) to substitute absent init values,

class Person {
  // ...
  public Person With(string name = this.Name, int age = this.Age) { ... }
}

So that something like person with { Name = "newName" } could be translated to:

(var _t = person; _t.With(name: "newName", _t.Age))

There is a lot more moving parts here but the simple initialization does not necessarily depend on it.

@DavidArno

Having a with expression modify the original data would be really bad in my view.

This is a known papercut that could simplify various applications and there's no alternatives for that either except for member-wise assignments.

@Odonno

We should stay with the immutable state of the with keyword.

The immutable state is not a property of With methods, so you could totally get around it as it's all user code, except if the class itself is defined as a "record".

I think it is not that useful if we have the strict null-check feature in C#8.

with? does not depend on NRT but it does affect it. We just produce a null check before member initialization and omit it in case of a null in LHS, much like the ?. operator.

@YairHalberstadt

It would be useful to have two different syntaxes.

I don't know what it buys you since there's no guarantee with pattern-based With methods, again, except if the class itself is defined as a "record".

alrz avatar Nov 28 '18 11:11 alrz

@alrz That's fair enough. I agree with you proposal in that case.

YairHalberstadt avatar Nov 28 '18 11:11 YairHalberstadt

@alrz

Actually the pattern-based with is not that simple since we first need caller-receiver default-argument (mentioned in the records proposal) to substitute absent init values

But this will get taken care of by the great and powerful overload resolution like any other optional arguments, won't it?

orthoxerox avatar Nov 28 '18 11:11 orthoxerox

I would very much like to see this added to a future language version to accomplish my example here regarding using the with statement when implementing methods with generics.

WhitWaldo avatar Apr 09 '23 01:04 WhitWaldo

#7752 - another case when this will be very convenient. (I don't force you to do this, it's all up to you)

Lavshyak avatar Dec 08 '23 18:12 Lavshyak