-1

The C# 9.0 with keyword facilitates the creation of new records from existing records.

My question is: what technical reasons did or could motivate introducing this specific feature with a new keyword and syntax instead of just generating a new function with named parameters.

For example consider the example from the docs:

    Person person1 = new("Nancy", "Davolio") { PhoneNumbers = new string[1] };
    Person person2 = person1 with { FirstName = "John" };

Instead we could have had something like:

    var person1 = new Person("Nancy", "Davolio", new string[1]);
    var person2 = person1.With(FirstName:"John");

I assume that adding new syntax to a widely used language like C# based on careful consideration and that possibly it is documented somewhere.

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
cdiggins
  • 17,602
  • 7
  • 105
  • 102
  • 1
    When you use an existing syntax, you have to adopt the same behavior as the existing semantics, or people get surprised/confused. – Ben Voigt Aug 20 '21 at 15:11
  • 3
    In particular in the named argument syntax, it's impossible for the callee to distinguish an omitted argument vs a caller explicitly passing the parameter's default value. This new language feature has to be able to distinguish. – Ben Voigt Aug 20 '21 at 15:13
  • 4
    A shame this was voted to be closed. The question is not opinion-based. Technical decisions about introducing new syntax into a language is usually carefully considered and debated in a public forum. – cdiggins Aug 20 '21 at 15:19
  • @BenVoigt: that sounds like a very plausible motivation. I am curious if there is a public record. – cdiggins Aug 20 '21 at 15:20
  • 4
    @cdiggins Why do you think something being carefully considered or debated in a public forum makes it not an opinion? People debate opinions publicly and consider them carefully all the time. That doesn't make them not opinions. This just isn't the appropriate place to debate such an opinion. There are plenty of places where it *is* appropriate to have such a debate. – Servy Aug 20 '21 at 15:20
  • 2
    @servy: This question doesn't ask for a debate on the opinion, it asks what opinion was held by the team making the decision. – Ben Voigt Aug 20 '21 at 15:22
  • 2
    I edited the question @servy. I don't mean to trigger a debate) just to understand what technical motivations could lead (or did lead, if it is documented) to introducing new syntax. There is very likely a reason that it was done this way. Syntax is not added to new languages arbitrarily. – cdiggins Aug 20 '21 at 15:23
  • @BenVoigt That's just false. It doesn't ask us what their opinion is (not that that's any better of a question, asking people to guess what someone else's opinion is is just as opinion based), it asks the reader what their opinion is. – Servy Aug 20 '21 at 15:24
  • 3
    i would look up the original github issue describing the use cases why the design team decided this. – Daniel A. White Aug 20 '21 at 15:24
  • 1
    @cdiggins It is no less opinion based now. It's not a question with an objective answer. You can't say that an answer is *correct* or *incorrect*, just whether or not it's an opinion you agree or disagree with. – Servy Aug 20 '21 at 15:26
  • 1
    @Servy: You are the one inserting the word "guess". – Ben Voigt Aug 20 '21 at 15:27
  • 1
    @DanielA.White : I don't know the process used by the C# language design group for adding and discussing new features, and how to look for it. Which motivated the question. I used to know how the C++ steering committee tackled such issues, and it was well documented, but hard to track all the languages. – cdiggins Aug 20 '21 at 15:27
  • @cdiggins https://github.com/dotnet/csharplang – Daniel A. White Aug 20 '21 at 15:28
  • 1
    @Servy: True and completely beside the point. The question is not asking for a guess. – Ben Voigt Aug 20 '21 at 15:29
  • https://github.com/dotnet/csharplang/blob/main/proposals/csharp-9.0/records.md#with-expression – Daniel A. White Aug 20 '21 at 15:29
  • @BenVoigt The only time I used the word "guess" was to say that *if* they asked us to guess at why someone else made a decision it wouldn't be appropriate. But they didn't ask that. They asked people for their opinion on how they would have designed the language. Both questions are close worthy, but the latter is what the OP asked. – Servy Aug 20 '21 at 15:29
  • @Servy: good point, I hope the edited question is less opinion based. There is surely a technical reason why the naive approach I describe is not feasible, and I now understand some motivations, which helps me understand the feature better. – cdiggins Aug 20 '21 at 15:30
  • 1
    @cdiggins Languages aren't designed the way they are because every decision has a right and a wrong objective answer. There are an enormous number of factors in *every* decision (making a question like "why was [language feature] designed the way it was too broad in addition to being opinion based) which people weigh *based on their opinions* and decide on a course of action. There are appropriate places to discuss those opinions, SO just isn't one of them. – Servy Aug 20 '21 at 15:35
  • 2
    Some of the language design discussion on GitHub: https://github.com/dotnet/csharplang/issues/3137 and https://github.com/dotnet/csharplang/blob/main/meetings/2020/LDM-2020-01-29.md – Matthew Watson Aug 20 '21 at 15:39
  • @MatthewWatson: your comment contains the answer IMO. Also special call out to BenVoigt though, I appreciate your insights. – cdiggins Aug 20 '21 at 15:48

2 Answers2

3

How would you implement this With method? Let's make a try:

public Person With(string firstName = null, string lastName = null,
                   string[] phoneNumbers = null)
{
    return new (
        firstName ?? this.FirstName,
        lastName ?? this.LastName,
        phoneNumbers ?? this.PhoneNumbers
    );
}

The problem with this approach is that we are not able to set a field to null as in

Person person2 = person1 with { phoneNumbers = null };

Therefore another approach is required. We could add overloads of every possible parameter combination

public Person With(string firstName);
public Person With(string lastName);
public Person With(string[] phoneNumbers);
public Person With(string firstName, string lastName);
public Person With(string firstName, string[] phoneNumbers);
public Person With(string lastName, string[] phoneNumbers);
public Person With(string firstName, string lastName, string[] phoneNumbers);

The problem is that the number of required overloads grows exponentially with the number of parameters:

2n - 1.


Yet another approach would be to let the compiler handle a call like person1.With(FirstName:"John"). It would be syntactic sugar for compiler generated code. This introduces other problems:

  • It does not behave as we would expect from other methods.
  • How would Intellisense display the parameters of this pseudo method? As in our first attempt? This would be misleading because of this nulling problem.
  • It could conflict with a user defined method of the same name.

Some interesting links relating to the implementation of Non-destructive mutation (with keyword):

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
  • This is a very insightful answer, thanks for taking the time to respond. I wonder if one can workaround it using Optional types as per https://stackoverflow.com/questions/24232053/how-to-declare-an-optional-type-in-c. – cdiggins Aug 20 '21 at 19:24
2

It was originally proposed with a With method.

enter image description here

In order to default parameter values from another object instance, there was a complicated new language feature proposed: caller-receiver parameters

The new design preserves the convenience of the old design but is much simpler for the compiler to implement.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720