Add design notes for nullable reference types

This commit is contained in:
Mads Torgersen 2017-12-20 17:05:17 -08:00
parent a0eaa09408
commit c112d27130
8 changed files with 317 additions and 247 deletions

View file

@ -1,15 +1,16 @@
# C# Language Design Notes for Aug 16, 2017
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
"It's an open question whether we go out with a bang`!`"
*Quote of the day:*
> "It's an open question whether we go out with a bang`!`"
## Agenda
1. The null-forgiving operator
# The null-forgiving operator
# The dammit operator
How exactly does the null-forgiving post-fix `!` operator work?
Proposal:
@ -24,107 +25,18 @@ Proposal:
- `GetNameOrNull()!.Length;`
3. *Default expressions*: if `T` is a non-nullable reference type, then `default(T)!` suppresses the warning normally given by `default(T)`
For 2, an alternative is to have a dedicated `!.` and `![...]` operator, cousins of `?.` and `?[...]`. Then you wouldn't get to factor out to a local with `var`.
For 2, an alternative is to have a dedicated `!.` and `![...]` operator, cousins of `?.` and `?[...]`. Then you wouldn't get to factor out to a local with `var`, though.
3 is a bit of a corner case. Most people would choose to just rewrite it to something else - there are plenty of options. But `default(T)` is a good strategy for code generators.
3 is a bit of a corner case. Most people would choose to just rewrite it to something else - there are plenty of options. But `default(T)` is a good strategy for code generators, so probably worth keeping the ability to silence that warning.
We could generalize to `!` silencing all nullability warnings even in subexpressions. No.
We could generalize `!` to silencing all nullability warnings even in subexpressions. This seems ill-motivated, though, and there's no particular expectation that you want silencing in subexpressions at the same time you want it on the overall expression.
If `!` is applied in a place that yields no nullability warnings, does that lead to a warning? No.
If `!` is applied in a place that yields no nullability warnings, does that lead to a warning? No. We don't want to create a new source of warnings caused by a warning-suppressing operator! There is a legit scenario, which is to clean up superfluous "!"s when a depended-upon API gets properly annotated. But this seems more the province of analyzers or similar tools.
We can make `!!` and error. If you really want to (we don't believe there's *any* scenario, other than swearing) you can parenthesize, `(e!)!`.
We can make `!!` an error. If you really want two conecutive bangs (we don't believe there's *any* scenario, other than swearing) you can parenthesize: `(e!)!`.
An alternative is to make the type of `e!` oblivious, if we choose to embrace a notion of oblivious. That's attractive in that it makes a type for "something that doesn't yield warnings", but it's also viral - could lead to many things not being checked. Option to be considered in future.
# Discussion about var and the type of nullable things known not to be null
``` c#
T t = ...;
if (t != null)
{
int s = t.ToString();
}
string? s = ...;
var s1 = s;
if (s != null)
{
var s2 = s;
int l = s.Length;
}
```
``` c#
string s1 = "Hello";
var s2 = s1; //
```
This could be safe:
``` c#
void M1(ref string s);
void M2(ref string? s);
var s = "foo";
if (s == null);
{
M1(ref s);
}
else
{
M2(ref s);
}
```
Three discussions:
1. What is the type of the value of a local variable `string? s` in a scope where it is known to be non-null? Does it contribute the type `string` to e.g. type inference, or does it remain `string?`
2. What is the meaning of `var`? Does it infer its nullability along with the rest of its type, from the initializer, or does it remain able to be assigned `null` even when initializer is not-null?
3. Should we reconsider completely abandoning the notion of nullness being part of a local's type at the top level?
1. could even consider for value parameters
``` c#
T M<T>(T t);
string? s = "abc";
var l = M(s).Length;
```
# Type inference
Proposal:
- Consider nullness an orthogonal aspect to the rest of the type being inferred
- If any contributing type is nullable, that should contribute nullness to the inference
- A `null` literal expression should contribute nullness to the inference, even though it doesn't otherwise contribute to the type
# Structs with fields of non-nullable type
Structs can be created without going through a declared constructor, all fields being set to their default value. If those fields are of non-nullable reference type, their default value will still be null!
It seems we can chase this in three ways:
1. Not at all. We just aren't that ambitious.
2. We warn on all fields of structs that have non-nullable reference types. That's a *lot*! How do you "fix" it? Make them nullable? No version of the `!` operator works here, since the whole point is you don't control initialization from user code.
3. We warn whenever a struct that has such fields is created as a default value. In other words, we treat the type the same as a non-null reference type, recursively. (And we warn on passing for a type parameter constrained by `new()` or `struct`?)
# Dotted names problems
An alternative is to make the type of `e!` oblivious, if we choose to embrace a notion of oblivious. That's attractive in that it makes a type for "something that doesn't yield warnings", but it's also viral - could lead to many things not being checked. It's an option to be considered in future.
## Conclusion
Follow the proposal. Make `!!` an error.

View file

@ -1,15 +1,19 @@
# C# Language Design Notes for Aug 23, 2017
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
## Agenda
We discussed various aspects of nullable reference types
1. How does flow analysis silence the warning
2. Problems with dotted names
3. Type inference
4. Structs with fields of non-nullable type
# How does flow analysis silence the warning
What exactly is the mechanism by which a nullable variable can be dereferenced without warning, when it's known not to be null?
``` c#
void M<T>(string? s, T t)
{
@ -24,22 +28,10 @@ However, this doesn't immediately work for type parameters. In the example above
An alternative mechanism is to say that the type of a variable and its value *does not change*. The null state tracking does not work through changing the type, but simply by directly silencing null warnings on "dangerous" operations.
This works for both `s` and `t` above.
``` c#
List<T> M<T>(T t) => new List<T>{ t };
void N(string? s)
{
if (s != null) WriteLine(M(s)[0].Length); // Warning or not?
if (s != null) { var l = M(s); l.Add(null); }
}
```
This works for both `s` and `t` above.
With the new proposal, you can better imagine separating out the null warnings to an analyzer, because the type system understanding of the `?` would be logically separated from the flow analysis.
It is also
IntelliSense will be a challenge:
``` c#
@ -47,7 +39,7 @@ void M(string s) => ...;
string? s = "Hello";
M(s); // Does IntelliSense confuse you here if the type of 's' is shown as 'string?'
M(s); // Does IntelliSense confuse you here if the type of 's' is shown as 'string?' ?
```
But there's non-trivial experience work in the IDE no matter what we do.
@ -62,7 +54,22 @@ var s = n; // 'string' or 'string?' ?
If null state affects the type, then `s` above is of type `string`, because `n` is known to be non-null at the point of assignment. If not, then it is `string?` (but currently known not to be null).
This also affects which type is contributed to generic type inference.
This also affects which type is contributed to generic type inference:
``` c#
List<T> M<T>(T t) => new List<T>{ t };
void N(string? s)
{
if (s != null) WriteLine(M(s)[0].Length); // 1
if (s != null) { var l = M(s); l.Add(null); } // 2
}
```
If the type of `s` changes to `string` in the non-null context, then the calls to `M` infer `T` to be `string`, and return `List<string>`. Thus, `//1` is fine, but `//2` yields a warning that `null` is being passed to a non-null type.
Conversely, if the type of `s` remains `string?` in a non-null context, then the calls to `M` infer `T` to be `string?`, and return `List<string?>`. Thus, `//2` is fine, but `//1` yields a warning about a possible null dereference.
## Impact on `!` operator
@ -88,8 +95,26 @@ We need to get more specific about exactly what it means that it "silences the w
Let's roll with the new approach. As always, we'll keep an eye on whether that leads to a good experience, and are willing to revisit.
# Dotted names problems
There are different approaches with different safeties:
- Don't track dotted names at all (safest): this pushes you to introduce a local for every null test of every field or property
- Track dotted names but invalidate when prefix is "manipulated" (passed or called a method on): doesn't catch aliasing or indirect mutation
- Track dotted names and assume it's still valid no matter how prefix is manipulated: bigger risk that it's wrong
The problem is that people have code today that checks on dotted names.
Should we have a dial? Otherwise we need to decide how we weigh safety vs convenience.
## Conclusion
In the prototype, since dotted names aren't implemented, let's try the more restrictive approach. People will let us know where this is too painful.
# Type inference
How are nullable reference types inferred?
Proposal:
- Consider nullness an orthogonal aspect to the rest of the type being inferred
@ -109,26 +134,5 @@ It seems we can chase this in three ways:
2. We warn on all fields of structs that have non-nullable reference types. That's a *lot*! How do you "fix" it? Make them nullable? No version of the `!` operator works here, since the whole point is you don't control initialization from user code.
3. We warn whenever a struct that has such fields is created as a default value. In other words, we treat the type the same as a non-null reference type, recursively. (And we warn on passing for a type parameter constrained by `new()` or `struct`?)
# Dotted names problems
There are different approaches with different safeties:
- Don't track dotted names at all (safest): pushes you to introduce a local
- Track dotted names but invalidate when prefix is "manipulated" (passed or called a method on): doesn't catch aliasing, indirect mutation
- Track dotted names and assume it's still valid no matter how prefix is manipulated: big risk that it's wrong
Problem is that people have code today that checks on dotted names.
Should we have a dial? Otherwise we need to decide how we weigh safety vs convenience.
``` c#
if (View.SuperView is var! superview) { // superview }
if !(View.SuperView is UIView superview) { return; }
```
(Say more about syntactic options)
In the prototype, since dotted names aren't implemented, let's try the more restrictive approach. People will let us know where this is too painful.
## Conclusion
The options to handle are painful. No conclusion.

View file

@ -1,100 +1,78 @@
# 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.
We looked at the Oct 4 design review feedback for nullable reference types, and considered how to react to it.
1. Philosophy
2. Switches
3. Dotted names
4. Type narrowing
5. Dammit operator type narrowing
6. Dammit operator stickiness
7. Array covariance
8. Null warnings
# 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.
Feedback: We should view this as a linting tool. We have to make most existing code pass muster, and it's ok with a little more complexity and forgiveness in the rules to achieve that.
## 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.
We agree with this general philosophy, and it is helpful to apply it to specific decisions.
# Switches
Feedback: Don't have many switches. Just on or off.
Off would mean suppress the warnings. The syntax would still be allowed.
Feedback: Don't have many switches and levers. Just "on" or "off". "Off" would suppress the warnings, but the `?` syntax would still be allowed. Make the hard decisions once and for all at the language level, rather than leave people with too many options.
## Conclusion
THis is a good philosophy. (We might allow Xtreme). We don't need to prototype the switch; it would always be on.
This is a good philosophy. We do think that there's possibly room for an "Xtreme" mode as well, for people that care more about catching more cases regardless of inconvenience.
# 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.
Feedback: We should track dotted names, and be very forgiving about what invalidates the null state. Otherwise it violates the general philosophy and complains about too much existing code. Things that *would* invalidate non-null-ness of a dotted chain is assigning to (or maybe passing to an out or ref parameter) the variable itself or any mutable prefix.
## Conclusion
Agree! If we adopt an Xtreme mode, this is probably one of the places where it would be harsher.
(Could go different with Xtreme mode)
# Type narrowing
Feedback: when a variable of a nullable reference type (e.g. `string?`) is known to not be null, we should consider its value to be of the narrower type `string`.
```
void M(string? n)
{
if (n == null) return;
var s = n; // string
var l = s.Length;
n = null; // ok
s = null; // ???
var s = n; // s is string, not string?
var l = s.Length; // ok
n = null; // ok
s = null; // warning
}
```
(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.
We previously abandoned this approach, because it doesn't work for e.g. type parameters, where we don't know if they are nullable reference types or not, and don't necessarily have an underlying non-nullable type to narrow *to*.
## Conclusion
We want to start treating the values of nullable variables with nonnull state as nonnullable in type inference.
This is one of those places where we should forego simplicity for friendliness. We should adopt a hybrid approach: When a type is a known nullable reference type, then having a non-null state *should* narrow its type. When it is a type parameter that might be instantiated with nullable reference types, then we can't narrow it, and should just keep track of its null state.
# Dammit operator type narrowing
Feedback: The dammit operator should also narrow the type of a nullable reference to be nonnullable.
``` c#
void M(string? n)
{
var s = n!; // s is string, not string?
var l = s.Length; // ok
}
T[] MakeArray<T>(T v)
{
return new T[] { v };
@ -102,55 +80,55 @@ T[] MakeArray<T>(T v)
void M(string? n)
{
var a = MakeArray(n!); // string[] ?
var l = a[0].Length; // ???
var a = MakeArray(n!); // a is string[], not string?[]
var l = a[0].Length; // ok
}
```
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.
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, but for now, `!` suppresses warnings *and* de-nullifies the type when it can.
# Dammit operator stickiness
Dammit operator lacks "stickiness" - you have to keep applying it (or introduce another local).
Feedback: The 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.
One 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.
Another 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?
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 `?`.
There are also cases where you *wouldn't* want it to be sticky, e.g. when you are using `!` to shut up a specific unannotated API that is lacking a `?`. Here you don't use `!` in the meaning of "this is really not null", but to the effect of "I am actually fine passing a null here". That meaning shouldn't really be contagious to subsequent lines.
## Conclusion
We don't know what, if anything, to do here.
We don't know what, if anything, to do here. For now we'll leave it as is.
# Array covariance
Feedback: Arrays are (unsafely) covariant over reference types, and for consistency we should also make them covariant over nullability.
## Conclusion
We'll think about this more.
This is probably right. We'll think about this more, but let's go with covariance for now.
# Null warnings
Agree with feedback
Feedback: Fine to warn in most places where a null value is given a nonnullable type, but we should beware of a "sea of warnings" effect. Specifically, we shouldn't warn on array creation, as in `new string[10]`, even though it creates a hole array of undesired nulls, because it is so common in current code, and couldn't have been done "safely" before.
# Libraries
## Conclusion
We agree. Xtreme mode, if we adopt that, may warn on array creation, though.

View file

@ -1,20 +1,29 @@
# 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!***
## Agenda
We went over the status of the prototype for nullable reference types, to address outstanding questions and make any last minute calls before release.
1. Constructors
2. Dotted names
3. Default expressions
4. Should we track null state for nonnullable ref types?
5. Inferred types for method type inference
6. Inferred nullability in hover tips
7. Smaller things not yet done
8. Unconstrained generics
9. Other issues
# Constructors
Currently the prototype doesn't look for `this(...)` constructor initializers. It should, and exempt constructors that do that.
Currently the prototype warns on a constructor that doesn't directly initialize all fields, even if it has a `this(...)` constructor initializer. It should exempt such constructors completely, since it will have required the called constructors to fully initialize.
We may consider a more nifty analysis later, at least for private constructors, but it doesn't seem high priority.
We may consider a more nifty analysis than that later, at least for private constructors, but it doesn't seem high priority for the prototype.
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.
We don't warn for the default constructor on structs. It's not really actionable, we think, since people can't write their own default constructor or add initializers to fields of structs. But this is worth revisiting later, probably in the context of Xtreme mode.
# Dotted names
@ -28,7 +37,7 @@ Should `default(string)` be of type `string?` or should it yield a warning *in a
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?`.
We're going to leave the current implementation, which makes `default(string)` be a `string?`.
Similar with `(string)null`. It's type is `string?`.
@ -38,16 +47,9 @@ Similar with `(string)null`. It's type is `string?`.
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
In the current implementation, the best common type and method type inference don't pick up *inferred* nullability, but only *declared* nullability.
``` c#
void M(string? s)
@ -57,31 +59,29 @@ void M(string? s)
}
```
There's a design question what is inferred when types differ only by nullability of type arguments
We can live with that as a known issue in the prototype.
# 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
Some warnings not given. That's alright, we'll give more in the future.
New constraints (`class?`, `object`) aren't added. That's 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.
Need to revisit to see if the weirdness we do is the right weirdness. Based on design review feedback, it is ok if what we do is not consistent with the rest of the language; the higher order bit is that it is helpful, intuitive and not obnoxious.
# 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?)
- No switch in the prototype; warnings are always on. We need to design the command line switch.
- Annotations for TryGet etc. still need to be designed.
- How to update BCL with annotations. (Automatically?)

View file

@ -6,11 +6,6 @@
## Agenda
# Nullable feedback
Great feedback. All over the map in blog comments, great quality in email feedback.
# Recursive pattern matching
## Grammar

View file

@ -1,9 +1,5 @@
# 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.
@ -15,19 +11,70 @@ We went over the feedback on the nullable reference types prototype, and discuss
5. Special methods
6. Filtering out nulls
# Interacting with existing, unannotated APIs
The most pressing problem for people using the prototype, is when existing, unannotated APIs are treated as all non-nullable. While you can live with that, there ends up being too many places where you have to use `!` to silence warnings.
Long term, of course the solution is for these APIs to evolve and to *get* annotations. That presents its own challenges: do people have to wait for them to update in place? Can we have a system of on-the-side annotations, either through reference assemblies or otherwise?
Short term, though, it seems that we should probably distinguish "legacy" APIs, and simply not warn based on their signatures. The way to recognize them is to bake an attribute into *new* assemblies - then *old* ones are simply the ones without that attribute.
There's design work to decide what exactly it means to "not warn on legacy signatures": do they represent a third type state ("between" nullable and non-nullable)? Does it travel with type inference? Etc.
# Accommodating alternative initialization patterns
The prototype warns when constructors do not initialize all non-nullable fields. However, this is too harsh for many usage patterns, where fields may be initialized by:
# Tracking nullable value types
- Initialization helpers called from constructors
- Factory methods that call constructors
- Object initializers, by convention
- Set-up/tear-down methods in test frameworks
- Reflection
We can try to do something more fancy to track initialization of fields through at least some of these. At the end of the day, there will be initialization that we just don't recognize, so there should also be a way to opt out of these warnings.
# Tracking dotted names
The prototype ended up not supporting the tracking of null-state for dotted names, and that was definitely felt by several prototype users, which goes to show that we do indeed need this functionality, as we suspected.
# Tracking nullable value types
There's some desire to have the same tracking of null-state for nullable *value* types. Not only could we allow you to dot through to the members of the underlying type (with some finagling to avoid breaking changes), but this would also be helpful when boxing the nullable value type.
# Special methods
Certain scenarios came up again and again, where utility methods or specific method patterns have special behavior regarding null. We need to design a general approach to this, where certain attributes on these methods can change their nullability behavior. Examples:
- `String.IsNullOrEmpty(s)`: `s` is not-null when method returns false
- `TryGet(out T x)`: `x` may-be-null when method returns false (even if `T` is non-nullable)
- `FirstOrDefault()`: result may-be-null (even if element type is non-nullable)
There's design work needed.
# Filtering out nulls
In this query it would be really good to know that the result is of non-null element type:
``` c#
var query =
from s in nullableStrings
where s != null
select s;
```
For query expressions we can maybe deal with this in the language. For the method syntax, it does not seem viable:
``` c#
var query =
nullableStrings.Where(s => s != null);
```
How would we know that the result is filtered by the lambda provided?
It's more likely that we can make a specialized `WhereNotNull` query method for this purpose.

View file

@ -0,0 +1,68 @@
# C# Language Design Notes for Dec 6, 2017
## Agenda
# Range
Several arguments against a generic `Span<T>`, such as conversions between them.
Step() should maybe be step with range
# Operator `..`
`op_Range` in IL, marked as special method.
Should it be target typed?
Example: `FloatRange`. If we don't supply it, no-one ever can, unless we make it target typed.
Conversion between ranges does not seem like a good idea: Even though it's technically possible, it might not make conceptual sense.
Target typing seems better. There's an issue with compat and ambiguity, where operator declarations on the operands and the result may clash. The proposal is to let the target type win. But that may not be necessary. The operator declared in the operands corresponds to the "natural type", and if there's an exact match to the target type, it would win.
For `IntRange` we get:
``` c#
IntRange r = 1 .. 10;
```
Here `..` is defined on the operand types, and when that one is applied, the natural type is `IntRange`, and that wins in overload resolution.
For `LongRange`:
``` c#
LongRange lr = 1 .. 10;
```
That works just dandy with target typing, but if there's also a conversion from `IntRange` to `LongRange` then there seem to be two competing conversions. We probably don't want that, and need to think through if that's the case, and if so how to amend it.
## Alternative 1
Look for an instance method (which can be an extension method), and have declared conversions and no target typing. That would lead to N x N extension methods and N x N conversion methods having to be declared to get the same "convenience".
But for the situation of having `x .. y` and *not* a target type, it would be simpler, in that it wouldn't need the `..` operator to be retrofitted to existing types.
## Alternative 2
Have a generic `Range<T>`. A range expression produces a range of the best common type of the operands. It can be target typed to another `Range<S>`, as long as there's a conversion from the end points to `S`. Comparison is done by expanding to use of `<=`, which better be defined.
# Wrap around
Let's make sure the library side doesn't allow foreach to throw on the boundary (`int.MaxValue`) or wrap around infinitely.
# Inclusive/exclusive
For floating point/non-enumerable, there's real expressiveness at stake. That's probably not the main scenario, though.
Problem with exclusive: I create a range from Sunday to Saturday, I need a name for the thing outside the range. Also, what if I need `int.MaxValue` as the top element?
Problem with inclusive: Going to array.Length. Slicing and windowing.
Evidence in the Mono code base for instance, shows both prevalent, with a slight overweight of inclusive.
# Range pattern

View file

@ -616,6 +616,25 @@ We looked at the interaction between generics and nullable reference types
3. Conversions between constructed types
## Aug 16, 2017
[C# Language Design Notes for Aug 16, 2017](LDM-2017-08-16.md)
1. The null-forgiving operator
## Aug 23, 2017
[C# Language Design Notes for Aug 23, 2017](LDM-2017-08-23.md)
We discussed various aspects of nullable reference types
1. How does flow analysis silence the warning
2. Problems with dotted names
3. Type inference
4. Structs with fields of non-nullable type
## Oct 4, 2017
[C# Language Design Review, Oct 4, 2017](LDM-2017-10-04.md)
@ -632,3 +651,50 @@ We looked at nullable reference types with the reviewers, Anders Hejlsberg and K
8. Null warnings
9. Special methods
10. Conclusion
## Oct 11, 2017
[C# Language Design Notes for Oct 11, 2017](LDM-2017-10-11.md)
We looked at the Oct 4 design review feedback for nullable reference types, and considered how to react to it.
1. Philosophy
2. Switches
3. Dotted names
4. Type narrowing
5. Dammit operator type narrowing
6. Dammit operator stickiness
7. Array covariance
8. Null warnings
## Nov 8, 2017
[C# Language Design Notes for Nov 8, 2017](LDM-2017-11-08.md)
We went over the status of the prototype for nullable reference types, to address outstanding questions and make any last minute calls before release.
1. Constructors
2. Dotted names
3. Default expressions
4. Should we track null state for nonnullable ref types?
5. Inferred types for method type inference
6. Inferred nullability in hover tips
7. Smaller things not yet done
8. Unconstrained generics
9. Other issues
## Nov 27, 2017
[C# Language Design Notes for Nov 27, 2017](LDM-2017-11-27.md)
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