Add LDM notes for Feb 26, 2020

This commit is contained in:
Andy Gocke 2020-03-03 10:04:08 -08:00
parent cf22e016c7
commit 1dbb8e82be
2 changed files with 129 additions and 4 deletions

View file

@ -0,0 +1,123 @@
# C# Language Design Meeting for Feb. 26, 2020
## Agenda
Design Review
## Discussion
Today is a design review, where we collect the design team, selected emeritus
members, and a number of broad ecosystem experts to provide some "in-the-moment"
feedback to our current designs and their directions.
Today we presented more of our thoughts on the top-level statements/"simple programs"
features and records.
### Simple Programs
We have a prototype of simple programs. As per the existing spec, you can have
top-level functions among all files, and other statements in a single file.
Collected feedback, not in any particular order:
* Supporting local functions in files other than the top-level statement file doesn't
seem useful and could cause confusion. If there's a local function defined in a file
only with classes, it would appear that that function would be in scope for the
classes, according to C# lexical scoping conventions. However, since these are defined
as *local functions*, not top-level methods, it would be an error to use them inside
the class. Moreover, even if that confusion is resolved, there doesn't seem to be a
compelling reason to allow it in the first place. Because these functions are not
accessible from anything except the main statement file, it would be most likely to
want to put the functions in that file, next to the uses. The only benefit from allowing
local functions in separate file may be as a helper file that is linked into other
compilations. However, wrapping these utility functions in a class so they can be used
in more places seems like a small burden with big benefits. Once wrapped in a class,
these functions are simply methods like in C# today.
* Mixing classes and statements in the same file could generate some confusion. The existing
design is that classes can see the variables created by statements, but it would be illegal
to reference them. This keeps the option open to allow access later. However, this could be
a confusing middle ground. To simplify the situation we could require only statements in the
top-level in one file (forcing all types to be declared in separate files). However, there is
interest in using utility classes in the top-level statements, perhaps especially with a
forthcoming records feature that provides simple, short syntax for declaring new types.
* Many of these features mirror what we already have in CSX. It's good that our
current designs are similar and allow these constructs in more places, but since the semantics of
this design have subtle differences from CSX this would effectively create a third dialect of C#.
There's some desire to unify these worlds, but it's difficult. CSX is designed to allow all
values to be persisted, which is important for the scripting "submission" system, but this makes
a number of types of statements illegal that we have support for in the current design, like
ref locals. It also creates a burden for new designs, where statements need to be explicitly
designed for both CSX and C#. For example, the new `using var` declaration form is nonsensical
under the CSX design and probably should be illegal. Since the current 'simple programs' design
effectively treats statements like they are part of a method body, there's a cleaner semantic
parallel with C#, meaning less special-casing.
### Records
Here we presented a variety of different pieces of designs we have been thinking about.
#### Nominal Record
Feedback:
* One of the biggest drawbacks of the current writeable-property style in C#, where types are
declared with public mutable properties that are then initialized using object initializers,
is that author can't enforce that certain properties are always initialized. It would be a
big disappointment if any "nominal records" feature that we built couldn't support this feature.
* With the design as-is there's no way to validate the whole state of the object. However, that's
also true of the object-initializer style currently in use, and this doesn't seem to be as a big
of a problem for current users.
#### Value Equality
* Positive feelings on generating `.Equals(T)` and implementing `IEquatable<T>`, mixed feelings
on generating the `==` and `!=` operators.
* If we opt-in the whole class using `value class`, we also need an opt-out for individual members.
Regular classes also often have somewhat specialized equality requirements, like wanting to compare
certain lists as sequence-equal, or compare strings ignoring case. This observation points to a
lot more customization points for value equality on general classes than value equality on records.
* Using value equality on mutable state is seems dangerous if the type is used in a dictionary,
but despite the danger, other languages (Java, Go) have value equality for common types, like
lists, that can be easily added to a dictionary.
* We don't currently have a robust mental model for what it means to be a "value class." Is "value
class" a separable concept from "implementing value equality," which people often do today? Or
is it not a different type of class, but simply a modifier signaling an implementation detail,
namely that the compiler generates value equality automatically. If we think of value equality
as a public contract, how does that change our view of existing code? Classes can currently
override Equals, but we don't distinguish what *kind* of Equals they provide. That isn't a
language concept, in a sense, but a part of the documentation.
### Nominal Records
* When using the `with` expression on nominal records, the generated parameter-less `With` method
looks a lot like `Clone`. It does little aside from return a new object with a shallow copy of
the state. If `With` is essentially Clone, why not use one of the existing forms of Clone that we
already have?
* ICloneable is deprecated and MemberwiseClone is protected. Maybe we should just call the method
Clone(), but not override or implement any of the other framework uses.
* This feature looks a lot like structural typing from other languages, like Javascript's "spread"
operator, but that is not the feature we're currently trying to build. This is feature is still
about declaring new types, not providing some compatibility between existing types.
* We spent a lot of time talking about validation and "validators", a very recent concept that was
floated as an alternative to constructors, executing after the `with` expression.
* There's some general concern about having no capability of validation, but no consensus on
exactly how validators should work.
* If validators work against the copied fields of the object, that seems to imply that the
fields are the state being operated on. On the other hand, only certain members can be
mentioned in the `with` expression. Why wouldn't those be the things that are copied? Instead
of all the state?

View file

@ -54,10 +54,6 @@
- Digest feedback from design review
- Records design (Mads, Andy)
## Feb 26, 2020
- Design review
## Jan 29, 2020
- Records: drilling in to individual features (Mads)
@ -70,6 +66,12 @@
Overview of meetings and agendas for 2020
## Feb 26, 2020
[C# Language Design Notes for Feb. 26, 2020](LDM-2020-02-26.md)
Design Review
## Feb 24
[C# Language Design Notes for Feb. 24, 2020](LDM-2020-02-24.md)