{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/3913","labels_url":"https://api.github.com/repos/dotnet/roslyn/issues/3913/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/roslyn/issues/3913/comments","events_url":"https://api.github.com/repos/dotnet/roslyn/issues/3913/events","html_url":"https://github.com/dotnet/roslyn/issues/3913","id":94527797,"number":3913,"title":"C# Design Notes for Jul 1, 2015","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"}],"state":"open","locked":false,"assignee":{"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},"milestone":{"url":"https://api.github.com/repos/dotnet/roslyn/milestones/8","html_url":"https://github.com/dotnet/roslyn/milestones/C%23%207%20and%20VB%2015","labels_url":"https://api.github.com/repos/dotnet/roslyn/milestones/8/labels","id":962632,"number":8,"title":"C# 7 and VB 15","description":"\r\n\r\n<!---\r\n@huboard:{\"order\":0.375}\r\n-->\r\n","creator":{"login":"gafter","id":3804346,"avatar_url":"https://avatars.githubusercontent.com/u/3804346?v=3","gravatar_id":"","url":"https://api.github.com/users/gafter","html_url":"https://github.com/gafter","followers_url":"https://api.github.com/users/gafter/followers","following_url":"https://api.github.com/users/gafter/following{/other_user}","gists_url":"https://api.github.com/users/gafter/gists{/gist_id}","starred_url":"https://api.github.com/users/gafter/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gafter/subscriptions","organizations_url":"https://api.github.com/users/gafter/orgs","repos_url":"https://api.github.com/users/gafter/repos","events_url":"https://api.github.com/users/gafter/events{/privacy}","received_events_url":"https://api.github.com/users/gafter/received_events","type":"User","site_admin":false},"open_issues":45,"closed_issues":4,"state":"open","created_at":"2015-02-02T23:41:34Z","updated_at":"2015-08-26T19:17:14Z","due_on":null,"closed_at":null},"comments":43,"created_at":"2015-07-12T02:56:22Z","updated_at":"2015-08-26T22:10:06Z","closed_at":null,"body":"# C# Design Meeting Notes for Jul 1, 2015\r\n\r\n## Agenda\r\n\r\nWe are gearing up to prototype the tuple feature, and put some stakes in the ground for its initial design. This doesn't mean that the final design will be this way, and some choices are arbitrary. We merely want to get to where a prototype can be shared with a broader group of C# users, so that we can gather feedback and learn from it.\r\n\r\n# Tuples\r\n\r\nThe tuples proposal #347 is pretty close to what we want, but has some open questions and unaddressed aspects.\r\n\r\n## Names\r\n\r\nWe want the elements of tuples to be able to have names. It is exceedingly useful to be able to describe which elements have which meaning, and to be able to dot into a tuple with those names.\r\n\r\nHowever, it is probably not useful to make the names too strongly a part of the type of a tuple. There is no really good reason to consider (int x, int y) an fundamentally different tuple type than (int a, int b). In fact, according to the analogy with parameter lists, the names should be of secondary importance: useful for getting at the elements, yes, but just as parameter lists with different parameter names can overwrite each other, as long as the types match at the given parameter positions, so should tuple types be considered equivalent when they have the same types at the same positions.\r\n\r\nAnother way to view it is that all tuples with the same types at the same positions share a common underlying type. We will make that type denotable in the language, in that you can write anonymous tuple types, `(int, int)`, even though you cannot write an anonymous parameter list.\r\n\r\nFor now we don't think we will allow partially named tuples: it is either all names or none.\r\n\r\n``` c#\r\n(int x, int y) t1 = ...;\r\n(int a, int b) t2 = t1; // identity conversion\r\n(int, int) t3 = t1;     // identity conversion\r\n```\r\n\r\nAll \"namings\" of a tuple type are considered equivalent, and are convertible to each other via an identity conversion. For type inference purposes, an inferred tuple type will have the names if all \"candidate types\" with names agree on them, otherwise it will be unnamed.\r\n\r\n``` c#\r\nvar a1 = new[]{ t1, t1 }; // infers (int x, int y)[], since all agree\r\nvar a2 = new[]{ t1, t2 }; // infers (int, int)[], since not all agree\r\nvar a3 = new[]{ t1, t3 }; // infers (int x, int y)[] since all with names agree\r\n```\r\n\r\nFor method overriding purposes, a tuple type in a parameter or return position can override a differently named tuple type. The rule for which names apply at the call site are the same as those used for named arguments: the names from the most specific statically known overload.\r\n\r\nTuple literals likewise come in named an unnamed versions. They can be target typed, but sometimes have a type of their own:\r\n\r\n``` c#\r\nvar t1 = (\"Hello\", \"World\");           // infers (string, string)\r\nvar t2 = (first: \"John\", last: \"Doe\"); // infers (string first, string last)\r\nvar t3 = (\"Hello\", null);              // fails to infer because null doesn't have a type\r\nvar t4 = (first: \"John\", last: null);  // fails to infer because null doesn't have a type\r\n\r\n(string, string) t5 = (\"Hello\", null);                           // target typed to (string, string)\r\n(string first, string last) t6 = (\"John\", null);                 // target typed to (string first, string last)\r\n(string first, string last) t7 = (first: \"John\", second: \"Doe\"); // error: when given, names must match up\r\n(string first, string last) t8 = (last: \"Doe\", first: \"John\");   // fine, values assigned according to name\r\n```\r\n\r\nThe last two are probably the only possibly controversial examples. When target typing with names in the literal, this seems very similar to using named arguments for a method call. These rules match that most closely.\r\n\r\nThis is something we may want to return to, as it has some strange consequences. For instance, if we introduce a temporary variable for the tuple, and do not use target typing:\r\n\r\n``` c#\r\nvar tmp = (last: \"Doe\", first: \"John\"); // infers (string last, string first)\r\n(string first, string last) t8 = tmp;   // assigns by position, so first = \"Doe\"\r\n```\r\n\r\nBut for now, let's try these rules out and see what they feel like.\r\n\r\n## Encoding\r\n\r\nAre core question is what IL we generate for tuples. The equivalence-across-names semantics make it easy to rely on a fixed set of underlying generic library types.\r\n\r\nWe want tuples to be value types, since we expect them to be created often and copied less often, so it's probably worthwhile avoiding the GC overhead.\r\n\r\nStrangely perhaps, we want tuples to be mutable. We think that there are valid scenarios where you want to keep some data in a tuple, but be able to modify parts of it independently without overwriting the whole tuple. Also, calling methods on readonly tuple members that are themselves value types, would cause those methods to be called on a throw-away copy. This is way too expensive - it means that there may be no non-copying way of doing e.g. value-based equality on such tuples.\r\n\r\nSo we encode each tuple arity as a generic struct with mutable fields. It would be very similar to the current Tuple<...> types in the BCL, and we would actively avoid purely accidental differences. For this reason, the fields will be called `Item1` etc. Also, tuples bigger than a certain arity (probably 8)  will be encoded using nesting through a field called `Rest`.\r\n\r\nSo we are looking at types like this:\r\n\r\n``` c#\r\npublic struct ValueTuple<T1, T2, T3>\r\n{\r\n    public T1 Item1;\r\n    public T2 Item2;\r\n    public T3 Item3;\r\n}\r\n```\r\n\r\nPossibly with a constructor, some `Equals` and `GetHashCode` overrides and maybe an implementation of `IEquatable<...>` and `IStructurallyEquatable<...>`, but at its core exceedingly simple.\r\n\r\nIn metadata, a named tuple is represented with its corresponding `ValueTuple<...>` type, plus an attribute describing the names for each position. The attribute needs to be designed to also be able to represent names in nested tuple types.\r\n\r\nThe encoding means that an earlier version of C#, as well as other languages, will see the tuple members as `Item1` etc. In order to avoid breaking changes, we should probably keep recognizing those names as an alternative way of getting at any tuple's elements. To avoid confusion we should disallow `Item1` etc. as names in named tuples - except, perhaps, if they occur in their right position.\r\n\r\n## Deconstruction syntax\r\n\r\nMost tuple features have a deconstruction syntax along the following lines:\r\n\r\n``` c#\r\n(int sum, int count) = Tally(...);   \r\n```\r\n\r\nIf we want such a syntax there are a number of questions to ask, such as can you deconstruct into existing fields or only newly declared ones?\r\n\r\nFor now we sidestep the question by not adding a deconstruction syntax. The names make access much easier. If it turns out to be painful, we can reconsider.\r\n\r\n## Other issues\r\n\r\nWe will not consider tuples of arity zero or one - at least for now. They may be useful, especially from the perspective of generated source code, but they also come with a lot of questions.\r\n\r\nIt seems reasonable to consider other conversions, for instance implicit covariant conversions between tuples, but this too we will let lie for now.  \r\n"},"comment":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/comments/135191269","html_url":"https://github.com/dotnet/roslyn/issues/3913#issuecomment-135191269","issue_url":"https://api.github.com/repos/dotnet/roslyn/issues/3913","id":135191269,"user":{"login":"asik","id":1631157,"avatar_url":"https://avatars.githubusercontent.com/u/1631157?v=3","gravatar_id":"","url":"https://api.github.com/users/asik","html_url":"https://github.com/asik","followers_url":"https://api.github.com/users/asik/followers","following_url":"https://api.github.com/users/asik/following{/other_user}","gists_url":"https://api.github.com/users/asik/gists{/gist_id}","starred_url":"https://api.github.com/users/asik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/asik/subscriptions","organizations_url":"https://api.github.com/users/asik/orgs","repos_url":"https://api.github.com/users/asik/repos","events_url":"https://api.github.com/users/asik/events{/privacy}","received_events_url":"https://api.github.com/users/asik/received_events","type":"User","site_admin":false},"created_at":"2015-08-26T22:10:06Z","updated_at":"2015-08-26T22:10:06Z","body":"It is easy to see how confusing mutable struct tuples would be in a language that defaults to reference semantics. This would be weird to most C# programmers:\r\n```\r\nvoid Foo(Tuple<int, string> tuple) {\r\n    // some code\r\n    tuple.Item2 = \"\"; // caller doesn't see the mutation, it's only local\r\n}\r\n```\r\nWith immutable tuples, if the programmer really wants a different local copy, he has to make this explicit, and now there's no confusion possible:\r\n```\r\nvoid Foo(Tuple<int, string> tuple) {\r\n     var localTuple = (tuple.Item1, \"\");\r\n}\r\n```\r\n\r\nOr let's say `arr` is an array of `Tuple<int, string>`:\r\n```\r\n    for (int i = 0; i < arr.Length; ++i)\r\n    {\r\n         var elem = arr[i];\r\n         elem.Item1 = 0;\r\n         elem.Item2 = \"\";\r\n    }\r\n```\r\nThis is reasonable-looking code that compiles without warning, but it doesn't actually mutate the elements of the items in the list; it only mutates the local variable `elem`. If tuple elements are readonly, this would fail to compile, and one has to write the correct:\r\n```\r\n    for (int i = 0; i < arr.Length; ++i)\r\n    {\r\n        arr[i] = (0, \"\"); // or whatever the syntax is for tuple creation\r\n    }\r\n```"}}