Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: default expression as a pattern #766

Closed
aluanhaddad opened this issue Jul 24, 2017 · 9 comments
Closed

Proposal: default expression as a pattern #766

aluanhaddad opened this issue Jul 24, 2017 · 9 comments

Comments

@aluanhaddad
Copy link

aluanhaddad commented Jul 24, 2017

While I have discussed a fair number of proposals, I have never written one myself, so take it easy on me if this is crazy 😆.

Proposal

A very minor, perhaps trivial annoyance with pattern matching currently is the restriction on what values can be used as patterns.

Proposal: allow the use of default as a constant-like pattern against which a value may be matched.

Currently proposals/patterns.md states the following

Patterns are used in the is operator and in a switch_statement to express the shape of data against which incoming data is to be compared. Patterns may be recursive so that parts of the data may be matched against sub-patterns.

pattern
    : type_pattern
    | constant_pattern
    | discard_pattern
    | var_pattern
    | recursive_pattern
    ;

I propose the following change

pattern
    : type_pattern
    | constant_pattern
    | discard_pattern
    | var_pattern
    | recursive_pattern
+   | default_pattern
    ;

Default Pattern

A default pattern tests the value of an expression against the default value of some type. The default must be the token default or default(T) for some T. If no type is specified, default represents the default value of the type of the expression. The pattern default is considered matching the expression e if object.Equals(default, e) would return true.

default_pattern
    : expression 'is' default
    : expression 'is' default(type)
    ;

Motivation

With the introduction of Generic patterns in C# 7.1, pattern matching has become much more broadly applicable and usable. This opens up powerful new scenarios and thereby exposes some minor limitations when writing generic code that uses patterns.

C# 7.1 also introduced the wonderful
Target-typed "default" literal
feature.

With these two features now in the language, the introduction of the proposed form would both increase (intuitive) consistency and provide a pleasant, although admittedly minor, syntactic sugar.

The Argument from (intuitive) Consistency

The following code is legal:

void M<T>(T x = default);

default in the above is used in a place where a compile time constant was required in previous versions of the language.

Example Use Case

My admittedly unsound IEqualityComparer<T> which provides for fuzziness, has the following GetHashCode implementation:

public sealed override int GetHashCode(T obj) =>
    EqualityComparer<T>.Default.Equals(obj, default) ? 0 : 1;

But with a Default Pattern I could write

public sealed override int GetHashCode(T obj) => obj is default ? 0 : 1;

Not much shorter, but it reads well.

Remarks

This may not be worth the effort given how minimal the gain is, but I wanted to throw it out there, the idea only occurred to me 20 minutes ago when I was refactoring something, so I wanted to post this before my feet either became frozen or my brain paralyzed with analysis.

@alrz
Copy link
Member

alrz commented Jul 24, 2017

I think default and default(T) both are already allowed under "constant pattern" (except for nullables which is most likely a bug: dotnet/roslyn#20015).

@aluanhaddad
Copy link
Author

@alrz interesting, thank you for linking your issue. From reading it I realized that it works fine for both reference types and for non-nullable value types. It also works for reference-type-constrained type parameters, but fails for value-type-constrained type parameters.

@alrz
Copy link
Member

alrz commented Jul 24, 2017

but fails for value-type-constrained type parameters.

AFAIK it should work for all cases because it's a constant. You could report it on roslyn repo as well.

@mattwar
Copy link
Contributor

mattwar commented Jul 25, 2017

I think obj is default is already supposed to work (or is supposed to work soon).

@DavidArno
Copy link

DavidArno commented Aug 2, 2017

@mattwar,

Just checked with VS2017 Update 3, Preview 10 and you are correct: obj is default is a valid expression in C# 7.1.

In addition, case (default): ... is also valid in a switch statement, but that might always have been the case.

@aluanhaddad
Copy link
Author

It works except in the context of generics without a reference constraint.

bool F<T>(T x) => x is default; // CS0150 A constant value is expected

the following works

bool F<T>(T x) where T: class => x is default;

@DavidArno
Copy link

@aluanhaddad,

Ah, my apologies. I didn't check it with generics. You are right, we aren't quite there yet with this one.

@alrz
Copy link
Member

alrz commented Dec 13, 2017

you may want to close this; default expression is now forbidden in patterns (dotnet/roslyn#23499).

@aluanhaddad
Copy link
Author

@alrz thank you for cross-referencing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants