-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Outline of C# 7 demo at MVP Summit 2015-11-02 #6505
Comments
@AnthonyDGreen FYI the features discussed here are working in https://github.com/gafter/roslyn/tree/features/patterns and will be demonstrated during the keynote tomorrow morning. |
Will there be a video later, for those of us not fortunate enough to attend the summit? |
Good summarization of planned features. I'd like to see the video from the keynote too. Examples are well chosen and understandable. However, I don't see any mention of user-defined operator |
Hopefully summit will be used as an occasion to release Visual Studio 2016 CTP1 😃. Unless C# is going to be updated independently via NuGet in future! |
I really like the pattern matching syntax |
Sorry, the keynote has NDA bits so no video can be published. |
@gafter Does the features/patterns branch also contain the implementation for local functions? |
@gafter exciting :-) oh why did I leave Redmond right before this? |
I did the demo from the branch https://github.com/gafter/roslyn/tree/features/patterns . That includes all the features currently in https://github.com/dotnet/roslyn/tree/future (binary literals, digit separators in literals, local functions) and a handful of features related to pattern matching (patterns, extended operator Right now there is no easy way for you to try out features from a branch inside Visual Studio, but we are working on making that possible. We expect it will be working in VS2015 RTM. |
Might be a stupid question, but how can you enable experimental features when compiling with csc.exe? Or how to enable it in general? When building from sources. |
If you try to use a feature that is not officially supported the error message will tell you what to do. For example, to enable pattern matching, you need to use the compiler flag I've also hacked in a feature on my branch (only) so that if you add |
Everything except |
Just saw that in the keynote today. First reaction when seeing the first Mind you, I think pattern matching is a nice addition to C#. It will be useful with record types and other things that don't inherit from the same class, when using complex conditions and deep structural matching. I just thought that the example presented was not great and started with code that I wouldn't want to see in our codebase in the first place. |
@jods4 You cannot add a virtual method for every piece of code in the world that wants their own specialized printed form. There is no way to "monkey-patch" C#. |
@gafter I agree with you. That said, for a general introduction of a new feature to a broad audience, I felt like the example used to demonstrate the feature was maybe not the best choice. There was no "constrained" context given (and there shouldn't) and the demo felt very much like your standard in-house LOB app, with the source code of three classes |
@jods4 Yes, it is typical in LDB applications to overuse object-oriented features, and take what should logically be code needed in one place locally and instead smear it across the type hierarchy in the form of virtual methods. There are many people so wedded to the object-oriented religion that they do not see the problem with this from a software engineering perspective. We're trying to make the C# language more usable for developers who want to keep local concerns local. |
@gafter I'm not willing to get into an argument... Here's just my point of view, from someone who has been creating small to large LOB apps in enterprises for the last 10 years. I actually do see the kind of code you used relatively often in LOBs (big
Say you need to add a new entity for teaching assistants (TA). Will you remember to go in I know object-oriented design is not a perfect fit for all problems. Even more so without advanced features such as double-dispatch. But OO is a successful paradigm that works well in many cases, inluding IMHO the one that was presented as a motivational example for the pattern matching feature. Out of curiosity, in few words, can you give example for problems cited in
BTW I think that the "multi-paradigms" approach of modern language design is great. I really enjoy the bits of functional programing that found their way into C# (OO at its core), they make C# awesome. Maybe a touch of AOP support would be cool. |
The Java compiler in version 2 was a quintessentially object-oriented program. There were class types for each syntactic construct in the language, and there were virtual methods for every phase of the compiler. As a result the code for each node type was huge and mixed concerns from across the whole compiler (because it contained virtual method implementations for every phase). In order to understand any particular phase of the compiler you'd have to read every source file (because the phase's virtual methods were spread across all the node types). It became a nightmare to maintain because you could hardly do anything without understanding everything. When we replaced it with a brand new compiler in version 3, it followed a more functional style. The node types contained their necessary data plus a couple of virtual methods to support the visitor pattern. Each phase of the compiler was completely contained in its own class and source file. The compiler was much, much easier to understand and maintain as a consequence. The total sources were between one third and one half the size of the previous compiler, and the compiler also implemented generics (which we didn't expose until version 5). Roslyn follows a design much more similar to the Java 3 compiler. Roslyn's data structures (syntax trees, semantic nodes, symbol tables, etc) do not expose virtual methods for all of the phases of the compiler. They are (mostly) fairly simple data holder classes. I do understand your concern about ensuring that changes to the type hierarchy are reflected in every switch statement that intends to handle all cases. When attempting to program in the functional style in a language such as Java or C# that does not support pattern-matching, you achieve that using the visitor pattern. For languages that do support the functional style, you achieve that using algebraic data types (ADTs). You can think of that as a way of declaring a set of types together, and then saying "that's all of them". Then you need some way to write a We are working on ADTs and completeness checking (See #188 and #6739) as part of this feature set. Due to time limitations it just wasn't shown as part of this demo. |
Having been in the demo, I can confirm that the description above is at least as comprehensive as what we saw. The only thing I can think of which was mentioned and related to the ongoing work on C# 7 was a note that it should be possible to declare local functions at the end of the method which uses them, such as the following. static void Main(string[] args)
{
Console.WriteLine(Fib(7));
Console.ReadKey();
int Fib(int n) => (n < 2) ? 1 : Fib(n - 1) + Fib(n - 2); //!
} This feature is not yet implemented in the compiler so it wasn't actually shown in the demo. On a side note, I was watching throughout the Summit for cases where a presentation contained information about work in the roslyn/corefx/coreclr repositories which was planned but never put in an issue for open discussion. I'm happy to say that I didn't see any such cases. For anyone actually following these repositories, everything we saw as "new" on these subjects at the Summit you were pretty well aware of months ago. |
@gafter I see where you're coming from. The right tool for the right job... Compilers have lots of similar "types" (in the form of AST nodes, etc.) and relatively fewer operations that they want to perform (type propagation and checking, optimizations passes, etc.). Moreover each operation really wants to be contained in a single unit (e.g. coding all the constant folding rules in one place). You can say the code is "operation" centric. LOB tend to have far fewer similar business "types" but relatively more "operations". They can easily be seen as more "data" centric (usually). What compilers do is a kind of Visitor pattern and I think it is telling that in 10 years I used the Visitor pattern several times, but never on business entities. And I think this sums it up: what really nagged me in the presentation, as a LOB developer, was that the example seemed like a LOB domain and the (short) code example did not seem appropriate if we had to code it in C# 6 today. I love that ADT are coming to C# with completeness. That makes them a compelling new option in our arsenal. Completeness is key, for reasons I gave in a previous comment above. With complex domains that you often don't master completely in large projects spanning many years, static guarantees of correctness is very much wanted! |
VS Connect 2015 - Short C# Pattern Matching demo https://channel9.msdn.com/Events/Visual-Studio/Connect-event-2015/010 Good stuff starts at 0:28:42 and ends at 0:33:06. |
Regarding pattern matching:
is there a need for:
Instead of something like
|
@SamirHafez You're asking if there should be an expression form of expression curly braces and semicolons don't make much sense for an expression context. Those make more sense for statements; for the expression form of |
I see. You're right, it doesn't really make much sense as a statement. However, is there not a chance |
Great stuff, thank you! |
It's interesting that you ask a very similar question to what I was about to post, save yours is the complete reverse to mine. I'd argue that (a) given C# 7 will support:
and (b) pattern matching is a functional language feature intended to supply polymorphic expression handling, why on earth are the C# 7 designers shoe-horning this feature into the imperative |
In most of the descriptions of how one might use pattern matching in C# 7, I come across statements like "Suppose I have a small hierarchy of types" and the example then uses a set of types that utilise that much hated by many feature, inheritance. In none of the examples I've seen so far has there been any mention of discriminated unions. Does anyone know whether C# 7 will only implement a poor-man's version of pattern matching that is limited to inheritance models, or will discriminated unions be implemented as part of the work being done on records? |
I'm new to pattern matching so I don't understand the idea behind: return p match (
...
case Student { Name is "Poindexter" } :
"A Nerd"
...
); What's the advantage over: return p match (
...
case Student s when s.Name == "Poindexter" :
"A Nerd"
...
); |
|
I'd argue that if there are two ways of doing the same thing and we have the option of introducing just one way, we should only introduce one way. (I don't really buy the naming issue or the accidental closure issue). If the first approach (matching on members) allows us to gain some much-needed functionality that the second doesn't, then that makes sense. It looks like it's probably required for user-defined is-operators |
In the property pattern all of the properties can be matched to any patterns, not just constant patterns, which can support much more complicated logic than could be expressed easily in a single guard expression: return p switch (
case Student { Course is OnlineCourse { Professor is TenuredProfessor { Name is var name } } } : name,
case * : "N/A"
); |
But they aren't really doing the same thing. The former matches a pattern and the latter matches a pattern and introduces a variable of a particular type (I'm not really convinced either), It's the kind of subtlety between the difference of linq statements and extension methods and I suspect will be regarded by future coders in similar ways. |
That's a really good analogy. I'd been wondering why have two ways of pattern matching the same thing. However, having both the query syntax and method-chain syntax for linq is really useful: sometimes one is the better solution to a task; other times the other way is the better. So maybe this'll prove the case with pattern matching too. |
There are unlimited ways of doing the same thing in a language. Obviously it doesn't solve a problem that you don't have. |
Some though (goto, switch, threads, inheritance, singletons...) take the problem you had and give you another to deal with... ;) |
Is there an answer to the question when the next design meeting will be and when we can read next meeting notes? Would be cool to see where you are currently and what's going on. Many thanks in advance. |
This is a really good question that I too would like to see the answer to. We really need more visibility of the design meetings and more regular design notes. Plus the documentation around the most likely new features in C# 7 could do with an overhaul and update to better reflect the current plans of the design team. Fingers crossed it all appears at some point this month. |
@robinsedlaczek, @DavidArno don't know if it's much and whether you seen this video but Mads Torgersen spoke about some of what's planned here https://blogs.msdn.microsoft.com/dotnet/2016/01/19/on-net-172016-mads-torgersen/ |
re: #6505 (comment) This response is a bit late but I wanted to comment that the one thing that I don't like about considering tuples in place of |
As for this static string PrintedForm(Person p)
{
if (p is Student s && s.Gpa > 3.5) //!
{
return $"Honor Student {s.Name} ({s.Gpa})";
}
else if (p is Student s)
{
return $"Student {s.Name} ({s.Gpa})";
}
else if (p is Teacher t)
{
return $"Teacher {t.Name} of {t.Subject}";
}
else
{
return $"Person {p.Name}";
}
} Can we just make it like Kotlin language that, in any block which contain So all we need is just static string PrintedForm(Person p)
{
if (p is Student && p.Gpa > 3.5) //p already become Student just after &&
return "Honor Student " + p.Name + " (" + p.Gpa + ")";
else if (p is Student) //p become Student in this block
return "Student " + p.Name + " (" + p.Gpa + ")";
else if (p is Teacher) //p become Teacher in this block
return "Teacher " + p.Name + " of " + p.Subject;
else return "Person " + p.Name;
} |
That would be a breaking change since existing code which performs comparisons like this might find the compiler resolving different members or overloads using the variable. |
Here is a pathological case that would work differently in existing code vs if that code worked the way you were suggesting:
|
Another would be: public class Person { }
public class Student : Person { }
public void DoSomething(Person person) { }
public void DoSomething(Student student) { }
Person person = ...;
if (person is Student) {
DoSomething(person);
} |
@HaloFour Then another possiblity is
Another functionality I would like compiler to have is, if we check null and try to use that object it should throw error. Could this be compiler capability without CLR support? such as
On the other hand
|
@Thaina The Non-nullable reference type (#227, #7445) flow analysis may be able to catch the suspicious use of |
@HaloFour We all know that if we use If we just And what I propose about null is simpler than that. I just want compiler checking instead of introduce whole new set of non-nullable reference type feature We all know that using null anywhere in C# will throw error for sure so if it obvious that any variable is null then it should be compile time error |
@Thaina Since the language allows overloading the boolean operators Person obj;
if(obj as Student && obj.Name == "Thaina") |
Point is there are lots of nasty little edge cases when it comes to changing semantics of any existing syntactically valid code. |
I used to deal with that problem often in C# 3.0 + .NET 2.0, by using the LinqBridge library. Similarly, one could define |
Design notes have been archived at https://github.com/dotnet/roslyn/blob/future/docs/designNotes/2015-11-02%20C%23%20Design%20Demo.md but discussion can continue here. |
Just have another idea that would make it work like kotlin maybe just this syntax object obj = "";
using(obj as string)
{
var length = obj.Length; // obj temporarily became string in this block
} This difference from normal |
Those scenarios are already handled by proposed type switching: object obj = "";
if (obj is string s) {
var length = s.Length;
} Don't see why there should be yet another way to do this, even if it would avoid a second identifier. |
Not really. The following is already valid code (and it works for any type that's implicitly convertible to object obj = …;
using (obj as IDisposable)
{
} So this would be a breaking change. |
@svick I forgot it could be used like that because it was state as "not a best practice", and have seen no one use it |
Here’s an outline of a demo at the MVP summit on 2015-11-02
Let’s talk about local functions.
A method often has other private “helper” methods that are used in its implementation. Those methods are in the scope of the enclosing type, even though they are only intended to be used in a single place. Local functions allow you to define a function where it is used. For example, given a helper method
Or, using the new syntax added in C# 6:
And the method that it is used in
In C# 7 you’ll be able to define the helper function in the scope where it is used:
Local functions can use variables from the enclosing scope:
You can imagine having to pass such state as additional parameters to a helper method if it were declared in the enclosing type, but local function can use local variables directly.
Capturing state like this does not require allocating frame objects on the heap as it would for delegates, or allocating a delegate object either, so this is much more efficient than what you would have to do to simulate this feature by hand.
Let’s talk about pattern matching.
With object-oriented programming, you define a virtual method when you have to dispatch an operation on the particular kind of object. That works best when the author of the types can identify ahead of time all of the operations (virtual methods) on the types, but it enables you to have an open-ended set of types.
In the functional style, on the other hand, you define your data as a set of types without virtual functions, and define the functions separately from the data. Each operation provides an implementation for each type in the type hierarchy. That works best when the author of the types can identify ahead of time all of the shapes of the data, but it enables you to have an open-ended set of operations.
C# does a great job for the object-oriented style, but the functional style (where you cannot identify all the operations ahead of time) shows up as a frequent source of awkwardness in C# programs.
Let’s get really concrete. Suppose I have a small hierarchy of types
The comments, by the way, shows a possible future syntax we are considering for C# 7 that we call records. We’re still working on records, so I won’t say more about that today. Here is an operation that uses these types
And for the purposes of the demo, a client of that operation
Note the need to declare the variables
s
andt
ahead of time inPrintedForm
. Even though they are only used in one branch of the series of if-then-else statements, they are in scope throughout. That means that you have to think up distinct names for all of these temporary variables. As part of the pattern-matching feature we are repurposing the “is” operator to take a pattern on the right-hand-side. And one kind of pattern is a variable declaration. That allows us to simplify the code like thisNow the temporary variables
s
andt
are declared and scoped to just the place they need to be. Unfortunately we’re testing against the typeStudent
more than once. Back to that in a moment.We’ve also repurposed the
switch
statement so that the case branches are patterns instead of just constants (though constants are one kind of pattern). That enables you to useswitch
as a "type switch":The compiler is careful so that we don’t type-test against
Student
more than once in the generated code forswitch
.Note the new
when
clause in the switch statement.We’re also working on an expression equivalent to the switch statement, which is like a multi-branch
?:
operator for pattern matching:Because you sometimes need to throw an exception when some condition is unexpected, we’re adding a throw expression that you can use in a match expression:
Another useful kind of pattern allows you to match on members of a type:
Since this is an expression, we can use the new “=>” form of a method. Our final method is
In summary:
match
expression1
in an ordinary switchStudent s
Student { Name is "Poindexter" }
*
The text was updated successfully, but these errors were encountered: