Add raw notes
This commit is contained in:
parent
95c9267d0d
commit
a0eaa09408
80
meetings/2017/LDM-2017-10-09.md
Normal file
80
meetings/2017/LDM-2017-10-09.md
Normal file
|
@ -0,0 +1,80 @@
|
|||
# C# Language Design Notes for Oct 9, 2017
|
||||
|
||||
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
|
||||
|
||||
|
||||
# 882 Negated if or negative patterns
|
||||
|
||||
Three approaches
|
||||
|
||||
1. bang outside if condition (then should I do that on while etc, too) `if !(o is int i)`
|
||||
2. negative patterns (but not very useful recursively) `not int i`
|
||||
3. `is not` as an expression operator
|
||||
|
||||
# 867
|
||||
|
||||
Avoid some statement cliffs...
|
||||
|
||||
Put it in X.X with a note to consider again when we have match expressions
|
||||
|
||||
# 414
|
||||
|
||||
There's a "there" there.
|
||||
|
||||
We think this should be addressed, and will keep the chanpioning issue to represent it.
|
||||
|
||||
However, it should be different:
|
||||
|
||||
1. It should not be strongly tied to the `Dictionary<K, V>` type, but be target typed
|
||||
2. We should look at initializing immutable objects (also for object and collection initializers)
|
||||
3. We already have index initializers. Are they good enough?
|
||||
|
||||
# 973 Declaration expressions
|
||||
|
||||
Last time, we had two issues:
|
||||
|
||||
1. Weren't ready to commit to scoping rules
|
||||
2. Weren't sure that we could get decent error recovery on syntax
|
||||
|
||||
1 is dealt with.
|
||||
2 was more that it was hard to show intellisense because more things were legal
|
||||
|
||||
Scenario is introduce a local variable in expressions without having to use trivial pattern matching. Also ref.
|
||||
|
||||
We feel like we need to spend more time with it to judge its value. 8.0 for now to trigger that discussion.
|
||||
|
||||
# 881 and 33
|
||||
|
||||
Fits with nullable in 8.0
|
||||
|
||||
# 185
|
||||
|
||||
Settle this in the 7.3 timeframe
|
||||
|
||||
# 187 Blittable
|
||||
|
||||
# 435
|
||||
|
||||
# 287
|
||||
|
||||
# 32
|
||||
|
||||
# 125
|
||||
|
||||
Missing, but not much ask for it
|
||||
|
||||
# 111
|
||||
|
||||
We would want to deal with single parameters. A problem is that discards do not shadow today, whereas identifiers do. We may want to change that.
|
||||
|
||||
# 191
|
||||
|
||||
Need more motivation
|
||||
|
||||
# 190
|
||||
|
||||
Some open design discussions
|
||||
|
||||
#
|
||||
|
||||
|
156
meetings/2017/LDM-2017-10-11.md
Normal file
156
meetings/2017/LDM-2017-10-11.md
Normal file
|
@ -0,0 +1,156 @@
|
|||
# C# Language Design Notes for Oct 11, 2017
|
||||
|
||||
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
|
||||
|
||||
|
||||
## Agenda
|
||||
|
||||
We looked at the design review feedback for nullable reference types.
|
||||
|
||||
|
||||
# Philosophy
|
||||
|
||||
Takeaways: Let unconstrained type parameters be its own special case; don't let it degrade the experience.
|
||||
|
||||
This is linting, make most existing code work, and don't shout at people too much.
|
||||
|
||||
## Conclusion
|
||||
agree, but let's consider a paranoid mode; "Xtreme". Could do that later.
|
||||
|
||||
Everybody who want the Xtreme mode would be happier with something than nothing. Should see what reaction is, and then dfecide whether to do it in-compiler, as analyzers or not at all.
|
||||
|
||||
|
||||
# Switches
|
||||
|
||||
Feedback: Don't have many switches. Just on or off.
|
||||
|
||||
Off would mean suppress the warnings. The syntax would still be allowed.
|
||||
|
||||
## Conclusion
|
||||
|
||||
THis is a good philosophy. (We might allow Xtreme). We don't need to prototype the switch; it would always be on.
|
||||
|
||||
|
||||
# Dotted names
|
||||
|
||||
Feedback is we should track dotted names, and be very forgiving about what invalidates null state.
|
||||
|
||||
Invalidation: assigning to a prefix, or passing it by out or ref.
|
||||
|
||||
## Conclusion
|
||||
|
||||
|
||||
|
||||
(Could go different with Xtreme mode)
|
||||
|
||||
# Type narrowing
|
||||
|
||||
```
|
||||
void M(string? n)
|
||||
{
|
||||
if (n == null) return;
|
||||
var s = n; // string
|
||||
var l = s.Length;
|
||||
n = null; // ok
|
||||
s = null; // ???
|
||||
}
|
||||
```
|
||||
|
||||
(Separate design discussion for IDE about what to show for n).
|
||||
|
||||
``` c#
|
||||
T[] MakeArray<T>(T v1, T v2)
|
||||
{
|
||||
return new T[] { v1, v2 };
|
||||
}
|
||||
|
||||
void M(string? n)
|
||||
{
|
||||
if (n == null) return;
|
||||
var a = MakeArray(n, n); // string[] or string?[] ?
|
||||
var l = a[0].Length; // ???
|
||||
}
|
||||
```
|
||||
|
||||
``` c#
|
||||
T N<T>(ref T r, T t)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void M(string? n)
|
||||
{
|
||||
if (n == null) return;
|
||||
var s = N(ref n, n);
|
||||
}
|
||||
```
|
||||
|
||||
This one is interesting because even though n goes in as an lvalue, the method cannot legally assign a null to it.
|
||||
|
||||
## Conclusion
|
||||
|
||||
We want to start treating the values of nullable variables with nonnull state as nonnullable in type inference.
|
||||
|
||||
|
||||
# Dammit operator type narrowing
|
||||
|
||||
``` c#
|
||||
T[] MakeArray<T>(T v)
|
||||
{
|
||||
return new T[] { v };
|
||||
}
|
||||
|
||||
void M(string? n)
|
||||
{
|
||||
var a = MakeArray(n!); // string[] ?
|
||||
var l = a[0].Length; // ???
|
||||
}
|
||||
```
|
||||
|
||||
Null suppression:
|
||||
|
||||
``` c#
|
||||
List<string?> l = GetList()!; // returns List<string>
|
||||
```
|
||||
## Conclusion
|
||||
|
||||
`! should keep its warning suppression, but also narrow the outermost type when it can, to match the new behavior when null-state is non-null.
|
||||
|
||||
There's hesitation because of the muddiness of using `!` for two different things. But we don't have a better idea. Explicit casts are quite verbose. We may need to revisit later.
|
||||
|
||||
|
||||
# Dammit operator stickiness
|
||||
|
||||
Dammit operator lacks "stickiness" - you have to keep applying it (or introduce another local).
|
||||
|
||||
Idea is to maybe have some top-level "assertion" that would declare a thing not-null for the whole scope.
|
||||
|
||||
Other idea is to have `s!` influence null state for flow analysis, staying "valid" for as long as a non-null state would have.
|
||||
|
||||
``` c#
|
||||
string? s = ...
|
||||
|
||||
if (s != null && s.Length == 5) ... // It flows here
|
||||
M((s!, s); // why not here?
|
||||
```
|
||||
|
||||
It would sort of make `s!` mean the same as `s = s!`.
|
||||
|
||||
There are also cases where you *wouldn't* want it to be sticky, e.g. when you are using `!` to shut a specific unannotated API that is lacking a `?`.
|
||||
|
||||
## Conclusion
|
||||
|
||||
We don't know what, if anything, to do here.
|
||||
|
||||
|
||||
# Array covariance
|
||||
|
||||
## Conclusion
|
||||
|
||||
We'll think about this more.
|
||||
|
||||
# Null warnings
|
||||
|
||||
Agree with feedback
|
||||
|
||||
# Libraries
|
80
meetings/2017/LDM-2017-10-16.md
Normal file
80
meetings/2017/LDM-2017-10-16.md
Normal file
|
@ -0,0 +1,80 @@
|
|||
# C# Language Design Notes for Oct 16, 2017
|
||||
|
||||
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
|
||||
|
||||
|
||||
"Being on the same floor as SPJ is a good way to shake out difficult corner cases"
|
||||
|
||||
1. LINQ
|
||||
2. Shapes
|
||||
|
||||
|
||||
# Applying to LINQ
|
||||
|
||||
Mixed results.
|
||||
|
||||
Looking for perf, and ways to do more selective specialization.
|
||||
|
||||
## Sum
|
||||
|
||||
Specialized to some numeric types, but not all. 500 lines of code. In those, the loop is unspecialized too.
|
||||
|
||||
One page of code!
|
||||
|
||||
A new design dimension: should I use interfaces or concepts: pay for abstraction or pay for specialization
|
||||
|
||||
Generic soup: this may be a superficial design issue, or even tooling issue.
|
||||
|
||||
For instance, the `AssociatedType`s, other languages allow them to be retrieved by dot notation. Jeremy Siek paper "Associated Types and ...". `TColl.TEnum` etc.
|
||||
|
||||
From experience, abstracting over enumerators tends to need associated types or higher-kinded types.
|
||||
|
||||
Shouldn't be too discouraged by being smoked by LINQOptimizer. That one optimizes big queries, but what keeps people away from LINQ is more the death by a thousand paper cuts of using LINQ all the time. Roslyn avoids things that allocate, which today means not using LINQ. THis could be the thing that would allow it to.
|
||||
|
||||
## Select
|
||||
|
||||
The return type is associated. The problem is that adding new instances can change the return type from afar, upsetting the consuming code.
|
||||
|
||||
Improves by 2/3rds when the array specialization is used.
|
||||
|
||||
## SelectMany
|
||||
|
||||
The generic type inference gets very messy here. It shows that concept inference needs to be interleaved with type inference in a way that we are still only loosely grasping.
|
||||
|
||||
This is a place where LINQ allocates a lot, whereas this allocates hardly anything. We go at .75 the time even unspecialized. Also, the specialized version is twice as fast as the unspecialized.
|
||||
|
||||
It shows that if you open up for specializations to be plopped in, there's quite a lot to gain.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Some promise on optimization. Pinches of salt here and there. The approach definitely seems to have promise.
|
||||
|
||||
More tests to do.
|
||||
|
||||
|
||||
# Shapes
|
||||
|
||||
Concepts can tie in to the richness of expression in C# around different kinds of operations (operators, conversions, constructors...)
|
||||
|
||||
Interesting to consider whether there's more of a specialization relationship between concepts and instances, rather than a type/instance relationship.
|
||||
|
||||
If this went further, there's a very large laundry list.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Conclusion
|
||||
|
||||
We *really* would like to be able to do the post-hoc implementation of concepts. This
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
93
meetings/2017/LDM-2017-10-18.md
Normal file
93
meetings/2017/LDM-2017-10-18.md
Normal file
|
@ -0,0 +1,93 @@
|
|||
# C# Language Design Notes for Oct 18, 2017
|
||||
|
||||
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
|
||||
|
||||
|
||||
## Agenda
|
||||
|
||||
|
||||
# 189
|
||||
|
||||
``` c#
|
||||
from s in strings
|
||||
let (b, i) = (int.TryParse(s, out var x), x)
|
||||
where b
|
||||
select i
|
||||
```
|
||||
|
||||
Problem is that out vars also aren't yet allowed.
|
||||
|
||||
We don't allow declaration expressions in
|
||||
|
||||
1. queries
|
||||
2. constructor initializers
|
||||
3. field/property initializers
|
||||
|
||||
All of these were because we didn't settle the scope question. Time to do that.
|
||||
|
||||
Let's put these as issues. And let's put those issues as 7.3. And let's put this one as 7.3.
|
||||
|
||||
|
||||
# 100
|
||||
|
||||
The difficulty of this depends on the level of ambition. It has to play in to applicability of overloads this is passed to. If we allow member initializers, then we need to bind those
|
||||
|
||||
``` c#
|
||||
M(new { X = 7, Y = new { A = "Hello" } });
|
||||
|
||||
x = new () { Y = { e1, e2 } };
|
||||
```
|
||||
|
||||
If we're lucky, this is just about whether the conversion exists or not.
|
||||
|
||||
Would there be a way that this would even influence generic type inference?
|
||||
|
||||
``` c#
|
||||
M(() => new (1));
|
||||
```
|
||||
|
||||
We'd need to think about this.
|
||||
|
||||
It may be that there's a subset of the feature that's simpler. We would need that subset to not preclude going further later.
|
||||
|
||||
Spooky action at a distance
|
||||
|
||||
``` c#
|
||||
M(Foo)
|
||||
M(Goo)
|
||||
|
||||
M(new (1))
|
||||
```
|
||||
|
||||
Goo has a constructor that takes an int. Adding such a constructor to Foo will break the code.
|
||||
|
||||
Now, adding a constructor is equivalent to adding a conversion in terms of the breaks it can entail.
|
||||
|
||||
May also need more betterness rules.
|
||||
|
||||
|
||||
It could be that it's better to limit the feature so it does not participate in type inference and betterness, and can always be checked as a conversion. It might even be worth considering it only specifically to where one target type is known (so no overload applicability).
|
||||
|
||||
|
||||
For now, let's put it in 8.0. We don't believe it's going to make 7.3, but that makes us still consider it for design time.
|
||||
|
||||
|
||||
# 179
|
||||
|
||||
For people who care about perf, they already need 3 or 4 overloads, and this would be yet another overload peopple will yell at them to add.
|
||||
|
||||
For no parameters today, you no longer need an overload to avoid allocation, because we now use `Array.Empty`.
|
||||
|
||||
`params IEnumerable<T>` would be even less performant than the array one, because enumeration allocates.
|
||||
|
||||
The point of it more is that if I want to take an `IEnumerable<T>` anyway, then it's a convenience to add params and take the arguments individually.
|
||||
|
||||
|
||||
Not important enough to prioritize time for the design soon. Let's make this X.X.
|
||||
|
||||
|
||||
# How to continue
|
||||
|
||||
Scan 7.X for remaining 7.3 items and ignore the rest for a bit.
|
||||
|
||||
|
111
meetings/2017/LDM-2017-10-25.md
Normal file
111
meetings/2017/LDM-2017-10-25.md
Normal file
|
@ -0,0 +1,111 @@
|
|||
# C# Language Design Notes for Oct 25, 2017
|
||||
|
||||
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
|
||||
|
||||
|
||||
|
||||
## #98
|
||||
|
||||
## 34 and 35
|
||||
|
||||
34 should bot be concurrent safe, just like the other compound assignment.
|
||||
|
||||
Bundle with 8.0, but could push out. Seems to align with nullable reference types
|
||||
# 32
|
||||
|
||||
Reconcile 32 with 1020
|
||||
|
||||
|
||||
Criteria:
|
||||
|
||||
- Loose ends
|
||||
- External expectation
|
||||
|
||||
# ref as iteration variable
|
||||
|
||||
Not currently allowed, should probably have a proposal (Andy)
|
||||
|
||||
# 185 keep in 3 to prioritize
|
||||
|
||||
# 45
|
||||
|
||||
Push out to 8.0 for realism, but still prioritize design time
|
||||
|
||||
# 933, 1046, and uninitialized ref local
|
||||
|
||||
These should happen together in 7.3
|
||||
|
||||
# 111 Punt to 8.X
|
||||
|
||||
# 1020 946 945 keep
|
||||
|
||||
# 882 pattern-related, goto 8.0
|
||||
|
||||
# 435
|
||||
Keep in 7.3, see if we can settle design
|
||||
|
||||
# 190
|
||||
|
||||
Relatively obvious design, with some gnarly bits (dynamic, conversion)
|
||||
|
||||
Usability gap with tuples let's keep it.
|
||||
|
||||
# 189
|
||||
|
||||
Let is more important than from. It lets you use out variables
|
||||
|
||||
``` c#
|
||||
from s in strings
|
||||
let t = (b: int.TryParse(out var n), n)
|
||||
where t.b
|
||||
select t.n
|
||||
```
|
||||
Could be
|
||||
``` c#
|
||||
from s in strings
|
||||
let (b, i) = (int.TryParse(out var n), n)
|
||||
where b
|
||||
select i
|
||||
```
|
||||
|
||||
There's a bit of design work, especially if we also want the from clause.
|
||||
|
||||
We could allow out vars but not the deconstruction, and it would still be useful.
|
||||
|
||||
Could save dec for later. It's actually orthogonal.
|
||||
|
||||
Action:
|
||||
|
||||
Carve out deconstruction, push to 8.X
|
||||
|
||||
# 187
|
||||
|
||||
On the brink, but keeping for now; need to be convinced of value
|
||||
|
||||
# 185
|
||||
|
||||
Keep pushing on it, got to get the train going on `Range`
|
||||
|
||||
`x..y` does new Range(x, y) or Range.Create(x, y)
|
||||
|
||||
Consider whether it should be a new kind of operator instead.
|
||||
|
||||
# 104
|
||||
|
||||
Micro-feature: Just allow `System.Enum` as a constraint
|
||||
Mini-feature: allow `enum` as a constraint, translate to `System.Enum, struct`
|
||||
|
||||
Keep this in, but it is very cuttable.
|
||||
|
||||
# 98
|
||||
|
||||
It's a zero-conceptual-overhead feature.
|
||||
|
||||
Original designers left in space between meanings for a purpose. But that's not so compelling to us anymore.
|
||||
|
||||
Because it touches overload resolution, it might be better alligned with a .0 release. But we're not compelled by that.
|
||||
|
||||
Let's keep it, but again, it's cuttable.
|
||||
|
||||
|
||||
|
54
meetings/2017/LDM-2017-11-06.md
Normal file
54
meetings/2017/LDM-2017-11-06.md
Normal file
|
@ -0,0 +1,54 @@
|
|||
# C# Language Design Notes for Nov 6, 2017
|
||||
|
||||
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
|
||||
|
||||
# Roslyn 20870
|
||||
|
||||
Protecting the client from unintended dependencies. But also protects from servicing. Today people going through reflection *know* they're being bad. Would this give enough sense that they are doing something special.
|
||||
|
||||
It would make consumers lazy about contacting the API owner about things they need exposed.
|
||||
|
||||
It would be an arms race - we would want the `IgnoreIgnore...` attribute to *really* protect things.
|
||||
|
||||
People will still have expectations about dependencies even if it was "their own fault" by using this attribute.
|
||||
|
||||
## Conclusion
|
||||
Too risky/fishy in too many ways.
|
||||
|
||||
|
||||
# Roslyn 17310
|
||||
|
||||
There is no good language level solution right now. This is better addressed with an analyzer, which can know specifically about SpinLock (for instance).
|
||||
|
||||
In time, when readonly struct declarations are added, as well as maybe the ability to declare individual struct members as readonly, *then* maybe we could start warn.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Not at the language level
|
||||
|
||||
|
||||
# Roslyn 20450
|
||||
|
||||
We have sympathy. It feels like a corner that's cut. But it's quite expensive to implement, and has semantic dark corners (`List<>.First.Foo`).
|
||||
|
||||
## Conclusion
|
||||
|
||||
Not now.
|
||||
|
||||
|
||||
# Roslyn 20015
|
||||
|
||||
When default-expressions are constant (according to the language) this is not interesting expressiveness - there's a literal you can use.
|
||||
|
||||
When they are *not* it gets a bit more interesting - you might want to check that your custom struct is zero-initialized. But you can do that with equality. Even in recursive scenarios, you can just `var`-pattern it and check in `when` or `&&`.
|
||||
|
||||
Additionally there is some concern about the target type being clear enough for the `default` expression.
|
||||
|
||||
## Conclusion
|
||||
|
||||
No.
|
||||
|
||||
|
||||
|
||||
|
||||
|
87
meetings/2017/LDM-2017-11-08.md
Normal file
87
meetings/2017/LDM-2017-11-08.md
Normal file
|
@ -0,0 +1,87 @@
|
|||
# C# Language Design Notes for Nov 8, 2017
|
||||
|
||||
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
|
||||
|
||||
|
||||
# Constructors
|
||||
|
||||
Currently the prototype doesn't look for `this(...)` constructor initializers. It should, and exempt constructors that do that.
|
||||
|
||||
We may consider a more nifty analysis later, at least for private constructors, but it doesn't seem high priority.
|
||||
|
||||
It's a warning per constructor. If it's on the implicit (default) constructor it goes on the class name. That's good.
|
||||
|
||||
|
||||
## struct initialization
|
||||
|
||||
We don't warn for the default constructor on structs. It's not really actionable, we think. But this is worth revisiting later, probably in the context of Xtreme mode.
|
||||
|
||||
|
||||
# Dotted names
|
||||
|
||||
Now work in the prototype, modulo a few bugs. (Not fully handling reassignment).
|
||||
|
||||
|
||||
# Default expressions
|
||||
|
||||
Should `default(string)` be of type `string?` or should it yield a warning *in and of itself*, and be of the type `string` (the type it states).
|
||||
|
||||
This is a question that's related to whether `string s = null` as a local declaration yields a warning.
|
||||
|
||||
We're going to leave the current impl, which makes `default(string)` be a `string?`.
|
||||
|
||||
Similar with `(string)null`. It's type is `string?`.
|
||||
|
||||
|
||||
# Should we track null state for nonnullable ref types?
|
||||
|
||||
Not now. Worth thinking about for later, as a stop gap.
|
||||
|
||||
|
||||
# Feedback
|
||||
|
||||
Email feedback.
|
||||
|
||||
Wiki page on GitHub, aka.ms link to it from blog
|
||||
|
||||
|
||||
# Inferred types for method type inference
|
||||
|
||||
Best type and method type inference, don't pick up inferred nullability, but only declared nullability
|
||||
|
||||
``` c#
|
||||
void M(string? s)
|
||||
{
|
||||
if (s == null) return;
|
||||
var a = new[] { s }; // string?[], should be string[]
|
||||
}
|
||||
```
|
||||
|
||||
There's a design question what is inferred when types differ only by nullability of type arguments
|
||||
|
||||
|
||||
# Inferred nullability in hover tips
|
||||
|
||||
Currently shows declared nullability. Relatively big work item, won't be fixed in prototype. So we need to set expectations.
|
||||
|
||||
The squiggle is the bottom line. :-D
|
||||
|
||||
|
||||
# Smaller things not yet done
|
||||
|
||||
Some warnings not given: That's alright, we'll give more in the future.
|
||||
New constraints (`class?`, `object`): Not blocking
|
||||
Variance: we'll deal with it when we get there.
|
||||
|
||||
|
||||
# Unconstrained generics
|
||||
|
||||
Need to revisit to see if the weirdness we do is the right weirdness. Doesn't have to be consistent with the rest of the language.
|
||||
|
||||
|
||||
# Other issues
|
||||
|
||||
- No switch in the prototype. Need to design the command line switch
|
||||
- Annotations for TryGet etc.
|
||||
- How to update BCL with annotations (automatically?)
|
||||
|
74
meetings/2017/LDM-2017-11-20.md
Normal file
74
meetings/2017/LDM-2017-11-20.md
Normal file
|
@ -0,0 +1,74 @@
|
|||
# C# Language Design Notes for Nov 20, 2017
|
||||
|
||||
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
|
||||
|
||||
|
||||
## Agenda
|
||||
|
||||
|
||||
# Nullable feedback
|
||||
|
||||
Great feedback. All over the map in blog comments, great quality in email feedback.
|
||||
|
||||
|
||||
# Recursive pattern matching
|
||||
|
||||
## Grammar
|
||||
|
||||
The grammar splits out to a couple of cases so that the right things are optional etc. directly in the grammar.
|
||||
|
||||
That separation is also there in the representation of the implementation, currently. An alternative is to have a single production and call it out in prose. There are advantages to having a separated representation, in that your code can make more assumptions about what's there or not.
|
||||
|
||||
## Names in deconstruction pattern
|
||||
|
||||
What is the utility? Current proposal it's only to guarantee that you get the thing you say. It does not allow reordering or leaving out elements.
|
||||
|
||||
This pattern should by and large work like deconstruction. But it's plausible to have names here, even if we don't in deconstruction; the argument would be a symmetry with constructors, which are sometimes used with names and optional arguments.
|
||||
|
||||
## Should the identifier be restricted from being a discard?
|
||||
|
||||
Since it can be left out completely? No, it's probably good to allow the discard. For refactoring etc.
|
||||
|
||||
## Matching via ITuple
|
||||
|
||||
It's "too likely" that the compiler would consider that a thing *may*
|
||||
implement `ITuple`.
|
||||
|
||||
We'll restrict to static types `object`, `ITuple` and any type that derives from `ITuple` and has no deconstructors.
|
||||
|
||||
`dynamic` is treated just like `object`. We don't go looking for deconstructors dynamically.
|
||||
|
||||
|
||||
## Syntactic ambiguity around parenthesized expression
|
||||
|
||||
Should we even have single-element deconstruction patterns?
|
||||
|
||||
Could require some other element to disambiguate, e.g. `(2) _`.
|
||||
|
||||
This would raise the cost of adding single-element tuples and deconstruction in the future, at least if they have a syntax *other* than parenthesized expressions (e.g., `(x,)`).
|
||||
|
||||
``` c#
|
||||
switch(my1DPoint)
|
||||
case 1DPoint(0):
|
||||
...
|
||||
case 1DPoint(var x):
|
||||
...
|
||||
```
|
||||
|
||||
Compromise position: Allow a single one only if there is a type in front. It gives the obvious symmetry with a single-element constructor, without restricting the design space for future single-element tuples or pattern grouping constructs.
|
||||
|
||||
## Cast ambiguity
|
||||
|
||||
Now went away. It's a cast, or an error.
|
||||
|
||||
## Short discard
|
||||
|
||||
Yes, allow `_` as a pattern in and of itself. It would not be allowed at the top level in an `is` expression. (That is allowed today, and designates the type `_`).
|
||||
|
||||
It actually means something different than `default` in a switch, because it gets an error if there are no cases left. That seems useful.
|
||||
|
||||
So: allow it everywhere except at the top level in an is-expression.
|
||||
|
||||
## Colon or is?
|
||||
|
||||
|
33
meetings/2017/LDM-2017-11-27.md
Normal file
33
meetings/2017/LDM-2017-11-27.md
Normal file
|
@ -0,0 +1,33 @@
|
|||
# C# Language Design Notes for Nov 27, 2017
|
||||
|
||||
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
|
||||
|
||||
|
||||
|
||||
## Agenda
|
||||
|
||||
We went over the feedback on the nullable reference types prototype, and discussed how to address the top issues that people had found using the feature on their own source code.
|
||||
|
||||
1. Interacting with existing, unannotated APIs
|
||||
2. Accommodating alternative initialization patterns
|
||||
3. Tracking nullable value types
|
||||
4. Tracking dotted names
|
||||
5. Special methods
|
||||
6. Filtering out nulls
|
||||
|
||||
# Interacting with existing, unannotated APIs
|
||||
|
||||
|
||||
# Accommodating alternative initialization patterns
|
||||
|
||||
|
||||
# Tracking nullable value types
|
||||
|
||||
|
||||
# Tracking dotted names
|
||||
|
||||
|
||||
# Special methods
|
||||
|
||||
|
||||
# Filtering out nulls
|
80
meetings/2017/LDM-2017-11-29.md
Normal file
80
meetings/2017/LDM-2017-11-29.md
Normal file
|
@ -0,0 +1,80 @@
|
|||
# C# Language Design Notes for Nov 29, 2017
|
||||
|
||||
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
|
||||
|
||||
# Match expression
|
||||
|
||||
Current proposal:
|
||||
|
||||
- Infix vs prefix `switch`
|
||||
- `switch` vs `match`
|
||||
- curlies or parens for body
|
||||
- `case` or not
|
||||
- arrows?
|
||||
- discards or defaults?
|
||||
- also need to remember when clauses
|
||||
|
||||
If it's too similar to switch statements, then it sets certain expectations. If it's too different, it's not utilizing existing intuition.
|
||||
|
||||
Should it be a verb/command? Not many expression keywords are that (`select` is an exception, though).
|
||||
|
||||
`case` is heavyweight, but helps visually separate the issues.
|
||||
|
||||
``` c#
|
||||
state = (state, action) switch (
|
||||
(DoorState.Closed, Action.Open) => DoorState.Opened,
|
||||
(DoorState.Opened, Action.Close) => DoorState.Closed,
|
||||
(DoorState.Closed, Action.Lock) => DoorState.Locked,
|
||||
(DoorState.Locked, Action.Unlock) => DoorState.Closed,
|
||||
_ => state);
|
||||
|
||||
state = match (state, action)
|
||||
{
|
||||
(DoorState.Closed, Action.Open) => DoorState.Opened,
|
||||
(DoorState.Opened, Action.Close) => DoorState.Closed,
|
||||
(DoorState.Closed, Action.Lock) => DoorState.Locked,
|
||||
(DoorState.Locked, Action.Unlock) => DoorState.Closed,
|
||||
_ => state
|
||||
};
|
||||
|
||||
state = switch (state, action)
|
||||
{
|
||||
case (DoorState.Closed, Action.Open): DoorState.Opened
|
||||
case (DoorState.Opened, Action.Close): DoorState.Closed
|
||||
case (DoorState.Closed, Action.Lock): DoorState.Locked
|
||||
case (DoorState.Locked, Action.Unlock): DoorState.Closed
|
||||
case _: state
|
||||
};
|
||||
```
|
||||
|
||||
The last one is subject to ambiguity-like situations betyween expression and statement `switch`.
|
||||
|
||||
We should also consider nesting of match expressions.
|
||||
|
||||
No matter what syntax we choose, we'll get requests for doing more things in expressions. We can live with that.
|
||||
|
||||
Parens look too much like a list of *expressions*.
|
||||
|
||||
Arrows make it look like lambda expressions.
|
||||
|
||||
## Decisions
|
||||
|
||||
We agree that we will not use the keyword `default`. You can use `_`, and in the rare case where that's defined, you can use `var _`.
|
||||
|
||||
We like curly braces for the grouping.
|
||||
|
||||
The rest is up in the air. We'll stay with the first version for now in the prototype, other than the curly braces.
|
||||
|
||||
|
||||
|
||||
# Where and when can identifier appear?
|
||||
|
||||
|
||||
|
||||
# Syntax for property patterns
|
||||
|
||||
Not urgent
|
||||
|
||||
``` c#
|
||||
|
||||
```
|
130
meetings/2017/LDM-2017-12-04.md
Normal file
130
meetings/2017/LDM-2017-12-04.md
Normal file
|
@ -0,0 +1,130 @@
|
|||
# C# Language Design Notes for Dec 4, 2017
|
||||
|
||||
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
|
||||
|
||||
QOTD: "Days since working around the JIT: 0"
|
||||
QOTD: "What's wrong with dangerous?"
|
||||
QOTD: "If we disallowed `Dangerous` in method names, Dangerous Dave wouldn't compile!"
|
||||
|
||||
# Foreach for Span and ReadOnlySpan
|
||||
|
||||
`Span` and `ReadOnlySpan` implement the foreach pattern with `GetEnumerator()` etc., even though they don't implement the `IEnumerable<T>` interface. (Interesting aside, the `Current` property is ref-returning, which the compiler is quite happy with.)
|
||||
|
||||
But it's faster to iterate them like an array, by using the `Length` and the indexer. We want to allow that for spans as well. We would keep the enumerable around, which also allows write access to the elements of a `Span`.
|
||||
|
||||
Interesting to consider opening up for a new `Length`/indexer `foreach` pattern, that's opted into e.g. with an attribute.
|
||||
|
||||
## Conclusion
|
||||
Definitely allow it as an optimization. The general case; not now, and maybe not ever. Better to limit it to compiler-known types, that are guaranteed to have equivalent semantics.
|
||||
|
||||
|
||||
# In vs value parameter ambiguity
|
||||
|
||||
If you have overloads
|
||||
|
||||
``` c#
|
||||
void M(in DateTime d) { }
|
||||
void M(DateTime d) {}
|
||||
|
||||
M(myDT); / ambiguity
|
||||
```
|
||||
|
||||
Options in tie breaker:
|
||||
|
||||
1. Choose based on LValue vs RValue
|
||||
2. val preferred
|
||||
3. Do nothing
|
||||
|
||||
How would you legitimately end up in this situation:
|
||||
|
||||
- Want to "change" to `in`, but must keep value for binary compat
|
||||
- But now you have source break
|
||||
|
||||
|
||||
We currently do 3. We should do 2. 1 is too arbitrary.
|
||||
|
||||
People might want to move existing calls to the `in` version, and we won't be helping them. But that's an analyzer.
|
||||
|
||||
Problem with operators. They can't use `in` when applied, so can't move away from val behavior. Oh well.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Go with 2. Roll out as bug fix.
|
||||
|
||||
|
||||
# Pattern-based fixed
|
||||
|
||||
Lots of types (especially new ones, like `Span`, `Memory` etc., but also `string`) would benefit from being `fixed`.
|
||||
|
||||
`Span<T>` currently has a `DangerousGetPinnableReference` method for this. We can make this a pattern.
|
||||
|
||||
It's a thin layer of syntactic sugar.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Let's do it. Extension methods are allowed (as always when we add new patterns). Let's aim for 7.3, but not super high pri.
|
||||
|
||||
|
||||
# this ref vs ref this
|
||||
|
||||
We allow `ref this` but not the other way around. That's wrong! We can't disallow `ref this`, but we should allow and prefer `this ref`.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Fix it. Go out as a bug fix.
|
||||
|
||||
|
||||
# Reachability, definite assignment and local functions
|
||||
|
||||
There's a bit of a mess around when captured variables in local functions are definitely assigned.
|
||||
|
||||
This is different from lambdas, because local functions take all calls into account.
|
||||
|
||||
The forthcoming ECMA spec changes the wording of these rules, so that they are not defined based on reachability.
|
||||
|
||||
Proposal:
|
||||
- Make beginning of local functions always reachable. (And lambdas, which is implemented but not spec'ed).
|
||||
- A captured local in a local function is considered definitely assigned if it's definitely assigned before every call of it.
|
||||
- That last one can be vacuously true.
|
||||
|
||||
## Conclusion
|
||||
|
||||
We like this. It could give errors in a few cases that don't today (including the bug report).
|
||||
|
||||
Is this an acceptable breaking change? In order to get an error, you'd have to have:
|
||||
- an unassigned local variable
|
||||
- an uncalled local function that uses it
|
||||
|
||||
Both are unlikely, undesirable and easily fixed. We'll check with compat council.
|
||||
|
||||
|
||||
# Equality operators on tuples
|
||||
|
||||
Needs to be defined by the language, in order to deal with long tuples, and to recursively apply `==` rather than `Equals`.
|
||||
|
||||
If somebody wrote their own `ValueTuple` *with* user defined equality, then this would break the use of that. That's not a supported scenario; you could get into the same kind of trouble with `Nullable<T>`.
|
||||
|
||||
For tuple literals, there is a tension between left-to-right evaluation and performance.
|
||||
|
||||
It seems that `t1 == t2` should mean the same as `t1 == (t2.Item1, t2.Item2)`. The upshot of the feature really is to deconstruct tuples (if they aren't already, by being tuple literals), then do point-wise comparison.
|
||||
|
||||
We will evaluate all operands left to right, recursively through tuple literals, and save to temps. We don't evaluate target-typed things, and we don't yet convert them.
|
||||
|
||||
Then we do point-wise `==` (or `!=`, in element position order, separated by `&&` (Or for `!=`, `||`). This may involve conversions. We are ok with those happening "late", and conditionally (won't happen if previous comparison failed), because conversions aren't usually expected to have side effects.
|
||||
|
||||
For dynamic we think we can allow it by just converting the result of, e.g. `d == e` (where `d` is dynamic) to `bool`.
|
||||
|
||||
Conversion from tuples matter, but not to tuples. If I have a `Foo` and a tuple, and `Foo` converts to tuple, then that won't help you.
|
||||
|
||||
## Conclusion
|
||||
|
||||
We think we have it, but will revisit during implementation.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in a new issue