{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/11205","repository_url":"https://api.github.com/repos/dotnet/roslyn","labels_url":"https://api.github.com/repos/dotnet/roslyn/issues/11205/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/roslyn/issues/11205/comments","events_url":"https://api.github.com/repos/dotnet/roslyn/issues/11205/events","html_url":"https://github.com/dotnet/roslyn/issues/11205","id":153895576,"number":11205,"title":"C# Design Notes for May 3-4, 2016","user":{"login":"MadsTorgersen","id":6677272,"avatar_url":"https://avatars.githubusercontent.com/u/6677272?v=3","gravatar_id":"","url":"https://api.github.com/users/MadsTorgersen","html_url":"https://github.com/MadsTorgersen","followers_url":"https://api.github.com/users/MadsTorgersen/followers","following_url":"https://api.github.com/users/MadsTorgersen/following{/other_user}","gists_url":"https://api.github.com/users/MadsTorgersen/gists{/gist_id}","starred_url":"https://api.github.com/users/MadsTorgersen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MadsTorgersen/subscriptions","organizations_url":"https://api.github.com/users/MadsTorgersen/orgs","repos_url":"https://api.github.com/users/MadsTorgersen/repos","events_url":"https://api.github.com/users/MadsTorgersen/events{/privacy}","received_events_url":"https://api.github.com/users/MadsTorgersen/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/dotnet/roslyn/labels/Area-Language%20Design","name":"Area-Language Design","color":"5319e7"},{"url":"https://api.github.com/repos/dotnet/roslyn/labels/Design%20Notes","name":"Design Notes","color":"fc2929"},{"url":"https://api.github.com/repos/dotnet/roslyn/labels/Language-C%23","name":"Language-C#","color":"207de5"},{"url":"https://api.github.com/repos/dotnet/roslyn/labels/Language-VB","name":"Language-VB","color":"207de5"},{"url":"https://api.github.com/repos/dotnet/roslyn/labels/New%20Language%20Feature%20-%20Pattern%20Matching","name":"New Language Feature - Pattern Matching","color":"bfd4f2"},{"url":"https://api.github.com/repos/dotnet/roslyn/labels/New%20Language%20Feature%20-%20Tuples","name":"New Language Feature - Tuples","color":"bfd4f2"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":19,"created_at":"2016-05-09T23:54:29Z","updated_at":"2016-05-18T20:22:18Z","closed_at":null,"body":"C# Design Notes for May 3-4, 2016\r\n=================================\r\n\r\nThis pair of meetings further explored the space around tuple syntax, pattern matching and deconstruction. \r\n\r\n1. Deconstructors - how to specify them\r\n2. Switch conversions - how to deal with them\r\n3. Tuple conversions - how to do them\r\n4. Tuple-like types - how to construct them\r\n\r\nLots of concrete decisions, that allow us to make progress on implementation.\r\n\r\nDeconstructors\r\n==============\r\n\r\nIn #11031 we discussed the different contexts in which deconstruction should be able to occur, namely deconstructing *assignment* (into existing variables), *declaration* (into freshly declared local variables) and *patterns* (as part of applying a recursive pattern).\r\n\r\nWe also explored the design space of how exactly \"deconstructability\" should be specified for a given type, but left the decision open - until now. Here's what we decided - and why. We'll stick to these decisions in initial prototypes, but as always are willing to be swayed by evidence as we roll them out and get usage.\r\n\r\n***Deconstruction should be specified with an instance (or extension) method***. This is in keeping with other API patterns added throughout the history of C#, such as `GetEnumerator`, `Add`, and `GetAwaiter`. The benefit is that this leads to a relatively natural kind of member to have, and it can be specified with an extension method so that existing types can be augmented to be deconstructable outside of their own code.\r\n\r\nThe choice limits the ability of the pattern to later grow up to facilitate \"active patterns\". We aren't too concerned about that, because if we want to add active patterns at a later date we can easily come up with a separate mechanism for specifying those.\r\n\r\n***The instance/extension method should be called `Deconstruct`***. We've been informally calling it `GetValues` for a while, but that name suffers from being in too popular use already, and not always for a similar purpose. This is a decision we're willing to alter if a better name comes along, and is sufficiently unencumbered.\r\n\r\n***The Deconstruct method \"returns\" the component values by use of individual out parameters***. This choice may seem odd: after all we're adding a perfectly great feature called tuples, just so that you can return multiple values! The motivation here is primarily that we want `Deconstruct` to be overloadable. Sometimes there are genuinely multiple ways to deconstruct, and sometimes the type evolves over time to add more properties, and as you extend the `Deconstruct` method you also want to leave an old overload available for source and binary compat.\r\n\r\nThis one does nag us a little, because the declaration form with tuples is so much simpler, and would be sufficient in a majority of cases. On the other hand, this allows us to declare decomposition logic *for* tuples the same way as for other types, which we couldn't if we depended on tuples for it!\r\n\r\nShould this become a major nuisance (we don't think so) one could consider a hybrid approach where both tuple-returning and out-parameter versions were recognized, but for now we won't.\r\n\r\nAll in all, the deconstructor pattern looks like one of these:\r\n\r\n``` c#\r\nclass Name\r\n{\r\n    public void Deconstruct(out string first, out string last) { first = First; last = Last; }\r\n    ...\r\n}\r\n// or\r\nstatic class Extensions\r\n{\r\n    public static void Deconstruct(this Name name, out string first, out string last) { first = name.First; last = name.Last; }\r\n}\r\n```\r\n\r\n\r\nSwitch conversions\r\n==================\r\n\r\nSwitch statements today have a wrinkle where they will apply a unique implicit conversion from the switched-on expression to a (currently) switchable type. As we expand to allow switching on any type, this may be confusing at times, but we need to keep it at least in some scenarios, for backwards compatibility.\r\n\r\n``` c#\r\nswitch (expr) // of some type Expression, which \"cleverly\" has a user defined conversion to int for evaluation\r\n{\r\n    case Constant(int i): ... // Won't work, though Constant derives from Expression, because expr has been converted to int\r\n    ...\r\n}\r\n```\r\n\r\nOur current stance is that this is fringe enough for us to ignore. If you run into such a conversion and didn't want it, you'll have to work around it, e.g. by casting your switch expression to object.\r\n\r\nIf this turns out to be more of a nuisance we may have to come up with a smarter rule, but for now we're good with this.\r\n\r\n\r\nTuple conversions\r\n=================\r\n\r\nIn #11031 we decided to add tuple conversions, that essentially convert tuples whenever their elements convert - unlike the more restrictive conversions that follow from `ValueTuple<...>` being a generic struct. In this we view nullable value types as a great example of how to imbue a language-embraces special type with more permissive conversion semantics.\r\n\r\nAs a guiding principle, we would like tuple conversions to apply whenever a tuple can be deconstructed and reassembled into the new tuple type:\r\n\r\n``` c#\r\n(string, byte) t1 = ...;\r\n(object, int) t2 = t1;     // Allowed, because the following is:\r\n(var a, var b) = t1;       // Deconstruct, and ...\r\n(object, int) t2 = (a, b); // reassemble\r\n``` \r\n\r\nOne problem is that nullable value type conversions are rather complex. They affect many parts of the language. It'd be great if we could make tuple conversions simpler. There are two principles we can try to follow:\r\n\r\n1. A tuple conversion is a specific *kind* of conversion, and it allows specific *kinds* of conversions on the elements\r\n2. A tuple conversion works in a given setting if all of its element conversions would work in that setting\r\n\r\nThe latter is more general, more complex and possibly ultimately necessary. However, somewhat to our surprise, we found a definition along the former principle that we cannot immediately poke a hole in:\r\n\r\n> An *implicit tuple conversion* is a standard conversion. It applies between two tuple types of equal arity when there is *any* implicit conversion between each corresponding pair of types. \r\n\r\n(Similarly for explicit conversions).\r\n\r\nThe interesting part here is that it's a standard conversion, so it is able to be composed with user defined conversions. Yet, its elements are allowed to perform their own user defined conversions! It feels like something could go wrong here, with recursive or circular application of user defined conversions, but we haven't been able to pinpoint an example.\r\n\r\nA definition like this would be very desirable, because it won't require so much special casing around the spec.\r\n\r\nWe will try to implement this and see if we run into problems.\r\n\r\n\r\nTuple-like construction of non-tuple types\r\n==========================================\r\n\r\nWe previously discussed to what extent non-tuple types should benefit from the tuple syntax. We've already decided that the deconstruction syntax applies to any type with a deconstructor, not just tuples. So what about construction?\r\n\r\nThe problem with allowing tuple literal syntax to construct any type is that *all* types have constructors! There's no opt-in. This seems too out of control. Furthermore, it doesn't look intuitive that any old type can be \"constructed\" with a tuple literal:\r\n\r\n``` c#\r\nDictionary<int, string> d = (16, EqualityComparer<int>.Default); / Huh???\r\n```\r\n\r\nThis only seems meaningful if the constructor arguments coming in through a \"tuple literal\" are actually the constituent data of the object being created.\r\n\r\nFinally, we don't have syntax for 0 and 1-tuples, so unless we add that, this would only even work when there's more than one constructor argument to the target type.\r\n\r\nAll in all, we don't think tuple literals should work for any types other than the built-in tuples. Instead, we want to brush off a feature that we've looked at before; the ability to omit the type from an object creation expression, when there is a target type:\r\n\r\n``` c#\r\nPoint p = new (3, 4); // Same as new Point(3, 4)\r\nList<string> l1 = new (10); // Works for 0 or 1 argument\r\nList<int> l2 = new (){ 3, 4, 5 }; // Works with object/collection initializers, but must have parens as well.\r\n```\r\n\r\nSyntactically we would say that an object creation expression can omit the type when it has a parenthesized argument list. In the case of object and collection initializers, you cannot omit both the type and the parenthesized argument list, since that would lead to ambiguity with anonymous objects.\r\n\r\nWe think that this is promising. It is generally useful, and it would work nicely in the case of existing tuple-like types such as `System.Tuple<...>` and `KeyValuePair<...>`.\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/comments/220146334","html_url":"https://github.com/dotnet/roslyn/issues/11205#issuecomment-220146334","issue_url":"https://api.github.com/repos/dotnet/roslyn/issues/11205","id":220146334,"user":{"login":"cowsen","id":18681977,"avatar_url":"https://avatars.githubusercontent.com/u/18681977?v=3","gravatar_id":"","url":"https://api.github.com/users/cowsen","html_url":"https://github.com/cowsen","followers_url":"https://api.github.com/users/cowsen/followers","following_url":"https://api.github.com/users/cowsen/following{/other_user}","gists_url":"https://api.github.com/users/cowsen/gists{/gist_id}","starred_url":"https://api.github.com/users/cowsen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cowsen/subscriptions","organizations_url":"https://api.github.com/users/cowsen/orgs","repos_url":"https://api.github.com/users/cowsen/repos","events_url":"https://api.github.com/users/cowsen/events{/privacy}","received_events_url":"https://api.github.com/users/cowsen/received_events","type":"User","site_admin":false},"created_at":"2016-05-18T20:22:18Z","updated_at":"2016-05-18T20:22:18Z","body":"Providing C# syntax for functional concepts (e.g. adding matching within the switch syntax) may make it more approachable for C# programmers, but you should also consider providing alternative functional syntax (i.e more terse syntax) for those with exposure to other functional languages."}}