-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
C# Design Notes for Feb 11, 2015 #1207
Comments
I wonder where this might leave other proposals such as #181, an RAII-like syntax for public void CopyToFile(Stream input, string fileName)
{
using FileStream output = new FileStream(fileName, FileMode.OpenOrCreate);
input.CopyTo(output);
} // output.Dispose() automatically called when output goes out of scope |
Agree with the decision to drop destructible types. As I said in that thread, I'd like to see a much more natural solution to the problem of deterministic memory (and other resource) cleanup. Doing that seems like it would almost by necessity require CLR changes. I don't think the lack of Intellisense for declaring the deconstruction of a tuple-returning method is a problem- it seems highly likely to me that people will use Supporting out parameters more naturally would definitely be an improvement. The framework methods that use Type equivalence- I think CLR changes are the best solution here. You guys have thrown around the idea for a long time of having better support for structural equality- it seems like building it into the CLR is the most general solution that would reach the widest user base (as then any CLR language could take advantage). Structural equality has been a huge success in TypeScript, I don't think we should shortchange C# some of the same benefits just to maintain downlevel compilation. |
@MadsTorgersen, I did not agree with the current design of tuples in the sense that it could only be used on the same assembly of its definition. If you work with other languages, like kotlin, scala, groovy etc, all these languages have a tuple type well defined, based on some generic type often, that can be used across any library (in our case, assembly). I understood your points, but if the tupple could not be used across assemblies, we could end up with the same problem of passing an anonymous type object throught a controller, in MVC, that will not be able to be used on the view, even if the model type is dynamic. I liked a lot about the definition, except that I don't think that tuples must be named. The definition of tuples just define an ordered sequence of values, not necessarily named. If you get implementations like that in kotlin or scala, you will see that it defines the tuple as just (int, string), for example. So to play with it we could just write: (int, string) x = (10, "hello");
// or
Tuple<int, string> x = ...; Then to deconstruct: var (number, someString) = x; // using type inference These types does not define a name for a member but I can get each value by using a property like: foreach (object value in tupleX) {
} Anyway, that is just my point of view. |
I think type equivalence in CLR is long overdue. It can be useful for tuples, delegates, type redirects and multiple other cases (e.g. ASP.NET's It doesn't have to be some ad-hoc auto-discovered equivalence though -- a solution based on explicit identifiers could work just as well, with compiler generating corresponding attributes.
I think unnamed tuples is just not a good fit for C#. They aren't good for Intellisense or API discovery (aside from obvious cases such as # Actual Perl API:
($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
$atime,$mtime,$ctime,$blksize,$blocks)
= stat($filename); Btw it seems that Kotlin have dropped nameless tuples: http://blog.jetbrains.com/kotlin/migrating-tuples/. |
TuplesI think you're overthinking it and trying to overdo it. Tuples should be just a grouping of nameless values. If you need to name them, deconstruct it. At first look, it might look like you need the names to understand the return value of this:
But a good API should already tell you what it does and what a method is returning. We have lived with that for simple return values and should be able to live with it with tuple return values.
No matter how strange it might look. However, local names for tuple parts might be usefull:
Having a tuple variable can be useful because you might be working on values returned from other methods:
Although deconstructing it would work just fine:
If local names are not given, the tuple members should always be possible through properties like This design keeps things simple and:
Don't try to make tuples the new anonymous types! Type equivalenceKeeping things simple allows to use the already exiting ConversionsOther than reordering, all the proposed convertions should be possible. Because names are only local and ephemeral, renaming is not an issue. But I would also like to have these:
(All the above rules should apply to either tuples, arrays and instances of the already existing
Out parametersWe are not here to be F#--! Although p/Invoke already allows out parameters to be converted to returning values and return HRESULTs converted into exceptions thrown, I don't think this is a good move to C#. If the proposed conversion is allowed, people will try to do this:
and not understand why they can't. With declaration expressions, almost the same can be achieved:
|
I think names are essential. For tuples like (int, int, int) relying only I also like the original syntactic sugar around out parameters. I never (var success, var parsed) = int.TryParse(stringValue); can be compiled into: int parsed; bool success = int.TryParse(stringValue, out parsed); without the need to allocate temporary tuple. I don't know if this 2015-03-13 10:05 GMT+01:00 Paulo Morgado [email protected]:
|
I agree- names are essential. Without names I'd argue against tuples being in the language. I absolutely do not want to see return signatures of tuple types without names. The likely mess of undocumented, confusing code that such a feature would cause would not be worth the benefit. |
Building on @paulomorgado 's suggestion.
Inline record declaration, perhaps transparent like anonymous types. |
@leppie I think that syntax is very confusing and would take a lot of getting used to. When reading that code, you'll be thinking something like:
|
@svick Advanced features require advanced users. |
I agree with paulomorgado. Tuple should not have member names, to be consistent with other popular languages like F#, Python, where members are by position rather than by name. In case where names are needed, you don't really need a tuple, but something like record class. With names, e.g.
Also, if C# used some custom implementation instead of System.Tuple<>, languages on .NET would be speaking different languages for tuple, which destroy interoperability. Designing tuple as another version of anonymous class is not a good choice. |
Using System.Tuple<> makes the feature unusable for many applications that care about performance. In order to freely take advantage of language-level tuple support, it needs to essentially be free from a performance perspective. I want a method returning a tuple to perform the same as if I'd passed a bunch of out parameters. Having every method call involving a tuple perform a heap allocation would be a non-starter. |
Tehicaly it ouldn't be that hard to load multiple values in the evaluation stack and have them deconstructed on the call site. The compiler could avoid instantiating the tuple if it was never used as a whole. But that wouldn't be CLS Compliant. |
Tuples (optionally) having names isn't without precedent, and I don't think that the comparison with anonymous types is unfair. Anonymous types are tuples, and proposal #1141 even aims to implement While Apple Swift allows tuples that have both named and unnamed properties, and they can be used effectively interchangeably. Those with unnamed properties are intended to be a private implementation and not to bleed into public specification. If tuples were to remain unnamed in C#, whether they are implemented on |
I'd prefer it be a struct type but I agree entirely with pualomorgado. public int DogYears CalculateDogYears(int humanYears){
///...
} Is a syntax error. I'd prefer it either be |
If tuples are limited to being unnamed only does the assembly boundary even matter? A public API like that would be just awful and I think should be discouraged strongly, to the point of the language not permitting it. If I saw the following signature in an assembly it would make me immediately reconsider using it: public (int, int) CalculateHumanAge(int dogYears) { ... } For many of the examples posted on this thread I think that names are important. |
Perhaps a name could appear but would not be compiled to il?
|
That doesn't solve the problem of the public surface of that API being awful. Encoding it as metadata in the form of attributes on the method is maybe a little better but requires that all consuming languages be supportive: public (int years, int months) CalculateHumanAge(int dogYears) { ... }
// ->
[TupleFieldName("years"),
TupleFieldName("months")]
public Tuple<int, int> CalculateHumanAge(int dogYears) { ... } That still doesn't seem to solve a lot of the issues with the use-cases, and you still get the performance penalty. Honestly, tuples being limited to just this does not seem terribly useful. |
What performance issue? Is it because it is a class type as opposed to a struct? |
Couldn’t the Tuple be re-written as a value type?
|
@mburbea Yep, @WolfieWerewolf Currently |
@HaloFour, where have you seen that "currently |
@HaloFour I don't know of any language that supports tuples where the tuples end up in a public api. |
Is that a reason not to do it?
|
@paulomorgado Ah crap, you're right. I was thinking of another lib I had worked with recently where tuple types did inherit like that. |
@WolfieWerewolf is that a reason not to implement tuples or? I'm not sure what you're asking. I believe that it's up to the individual developer his he wants to design his api. If he wants to return tuples all the way that's his decision. Of course we should try to discourage "wrong" use of a language feature but we must also recognize that almost any feature can be misused to some extent. |
@danfma We are leaning toward a solution that does unify across assembly boundaries. |
@Miista About returning tuples, it's quite common in Python. The accepted answer suggests that the named tuples are often preferred in Python over the plain ones. |
The argument for tuple parts needing names because of unintuitive method return values is interesting. We don't currently have any way of specifying a name for the return value of a method, and there are certainly plenty of ways to return an unintuitive value from a method. Such cases would be considered a code smell, the proper remediation of which would be to rename the method such that the return value is clear. Why would the same not be true of tuple return values? |
I agree with the comments saying its weird that return types would be named for tuples but are not named in the case of a single return value. However, I don't think the solution to this is to make tuples less useful by not having names. I think the natural solution would be allowing all return values to be named- it would both lead to more self-documenting code and provide parity with the specialness being proposed for tuples. |
But that doesn't change with named tuples. E.g. what you had previously: public SomeResult GetSomething() { ... }
var result = DoSomething();
if (result.Success)
Console.WriteLine(result.Value); What you have with tuples: public (bool success, int value) GetSomething() { ... }
var result = DoSomething();
if (result.success)
Console.WriteLine(result.value); Total amount of names is exactly the same aside from the type name. All I want is to avoid total regression where the property names are lost as well. |
For the
|
My thoughts too. Søren Palmund
|
I'd personally like to see both methods implemented. That covers both mathematic tuples and relational model tuples. If unnamed tuples was just a short-hand over Named tuples could just be inline record classes, with either convention or syntax to specify the name of the type. The problem here is type-equivalence as the BCL would treat each of those types as completely separate for now. Really, in my opinion, the implementation of the tuple is much less important than the language syntax to support working with them. I doubt that tuples in C# would enjoy the same degree of support as they do in languages like Python where they are fairly pervasive. I imagine that we'll probably see about the same level of support as with Apple Swift, with basic deconstruction into variables and probably pattern matching. |
@HaloFour : I don't think it is easy to make named tuples as simple syntax sugar over normal struct/class. But I fully agree with you that the syntax is much more useful than tuple itself. Like @MikePopoloski 's comment, we are more on the performance/maintainability side. Maybe what we want is not tuple at all, but a better syntax for However, I don't think |
They're going to be syntax sugar over struct/class either way simply because those are the only types possible in the CLR. It's not like you can push two values onto the evaluation stack and then pop them at the caller. The CLR doesn't allow that. The question is whether they use When dealing with named tuples as return types I envision the primary use would be to treat the return value as a single value and the named properties as properties. This is in line with how they're generally used in Apple Swift and Python: let http200Status = (statusCode: 200, description: "OK")
println("The status code is \(http200Status.statusCode)")
// prints "The status code is 200"
println("The status message is \(http200Status.description)")
// prints "The status message is OK" But I think that even with named tuples that the order of the components is important as I think that said tuple should be accessible by both name and index. It would also matter for destructuring expressions. let http404Error = (statusCode: 404, description: "Not Found")
let (code, message) = http404Error Or it can go the EcmaScript route where the named tuple properties are accessed by name within the destructuring assignment: var http204status = { statusCode: 204, description: "No Content" };
var { statusCode : code, description : message } = http204status;
var message = `The status code is ${code} meaning ${message}`; Personally I think that |
@HaloFour If Also, member order would become important for normal class/struct, reorder members can be a breaking change. |
Our current thinking is that we'll use a set of platform-wide structs analogous to the existing Tuple classes, and encode the field names as attributes (i.e. they will be erased in the runtime representation). Thus we would not need CLR support as @qrli fears. |
It's sad that CLR can support |
So that means that the following two snippets would be effectively equivalent: public (int quotient, int remainder) divmod(int numerator, int denominator)
{
int quotient = numerator / denominator;
int remainder = numerator % denominator;
return (quotient, remainder);
}
static void Main()
{
var result = divmod(5, 2);
Console.WriteLine("5 / 2 is {0} with a remainder of {1}", result.quotient, result.remainder);
} [TupleNames("quotient", "remainder")]
public STuple<int, int> divmod(int numerator, int denominator)
{
int quotient = numerator / denominator;
int remainder = numerator % denominator;
return new STuple<int, int>(quotient, remainder);
}
static void Main()
{
STuple<int, int> result = divmod(5, 2);
Console.WriteLine("5 / 2 is {0} with a remainder of {1}", result.Item1, result.Item2);
} |
@ashmind Well, it could if the function creating the struct supplied it with the names: return new STuple<int, int>("quotient", quotient, "remainder", remainder); Feels kludgy, but without CLR support for real tuples we're probably stuck with kludgy. |
@HaloFour The problem with that is that it would mean |
The design notes have been moved to https://github.com/dotnet/roslyn/blob/master/docs/designNotes/2015-02-11%20C%23%20Design%20Meeting.md Discussion on these design notes are in this issue. |
Check approach to destructible struct-s in dotnet/csharplang#4808 |
C# Design Meeting Notes for Feb 11, 2015
The design notes can be found at https://github.com/dotnet/roslyn/blob/master/docs/designNotes/2015-02-11%20C%23%20Design%20Meeting.md
Discussion on these design notes are in this issue.
The text was updated successfully, but these errors were encountered: