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: Inline type parameter constraints #7671

Closed
alrz opened this issue Dec 22, 2015 · 22 comments
Closed

Proposal: Inline type parameter constraints #7671

alrz opened this issue Dec 22, 2015 · 22 comments

Comments

@alrz
Copy link
Contributor

alrz commented Dec 22, 2015

I think in simple cases, inline constraints are far consite than where clauses, which cause to duplicate every type parameter.

void F<enum T>(T arg) {}
void G<struct T>(T arg) {}
void H<class T>(T arg) {}

class C<class T : IFoo> {}
interface I<out U, in class T : U> {}
@alrz
Copy link
Contributor Author

alrz commented Dec 22, 2015

Is this a no go?

@leppie
Copy link
Contributor

leppie commented Dec 22, 2015

Yes, no one commented in an hour, so probably a bad idea :D

@alrz
Copy link
Contributor Author

alrz commented Dec 22, 2015

@leppie lol, that was because I am myself a little uncertain about this. I just wanted to know if it is indeed a bad idea, then why. I know that every decision in language design had been carefully taken, so in C# 2.0 this probably had been considered and for some reason eventually dropped. on the other hand, this is like anonymous method / lambda expression all over again, changes of requirements cause to need for more concise syntaxes. My first motivation for this, is for nullable types, that said, it is more common to use struct and class constraints, but existing syntax feels a little, say, "dispersed", and even the fact that it applies in inverted lexical order makes it unintuitive, however this one T F<T>() is inevitable.

@leppie
Copy link
Contributor

leppie commented Dec 22, 2015

@alrz Was just kidding :) But it will probably make parsing a lot harder. If I understand correctly it would allow:

T F<S, struct T class : S : object>(...) { ... }

@alrz
Copy link
Contributor Author

alrz commented Dec 22, 2015

@leppie I think you meant T F<S : object, struct T>() { ... } As I said, it would be used for "simple" constraints, struct or class and potentially a single base type (however, you can't specify object as a constraint, replace it with any other type in this example).

@leppie
Copy link
Contributor

leppie commented Dec 22, 2015

@alrz simple-constraint recurses over type-parameter. It can go on forever. No one in their right mind is going to want to parse that :D

@alrz
Copy link
Contributor Author

alrz commented Dec 22, 2015

@leppie type-parameter is just an identifier, constraints are not part of that. (I was mentioning type-parameters actually, with a s).

@HaloFour
Copy link

It's not that it's a bad idea, it's that it doesn't offer anything really new or novel. The existing syntax isn't appreciably more verbose and while it is slightly removed from the type parameter declaration itself it remains a part of the type or member declaration.

@alrz
Copy link
Contributor Author

alrz commented Dec 23, 2015

@HaloFour Take this example,

void F<struct T>(T? arg) {}
// versus
void F<T>(T? arg) where T : struct {}

When I'm declaring this method, I know that T is going to be struct and therefore T? is just a Nullable<T> but in the current syntax it feels like an afterthought, you have to look at the end of method signature to find out what parameters actually are. Maybe simple-secondary-constraint is too much, but I think the the former just makes sense.

@HaloFour
Copy link

@alrz I don't disagree, I just don't think that said benefit warrants a second form of the syntax.

@alrz
Copy link
Contributor Author

alrz commented Dec 23, 2015

One more thing, with nullable reference types, it actually affects the method signature,

void F<struct T>(T? arg) {}
void F<class T>(T? arg) {}

The first one accepts a Nullable<T> but the second one just accepts a T.

@HaloFour
Copy link

That would be true regardless of this syntax.

void F<T>(T? arg) where T : struct { }
void F<T>(T? arg) where T : class { }

@alrz
Copy link
Contributor Author

alrz commented Dec 23, 2015

@HaloFour Yes, and until this very day (before #250), constraints weren't part of method signature and totally ignored in overload resolution, but they are now, somehow. Current syntax implies that they aren't really.

@HaloFour
Copy link

@alrz They aren't a part of the method signature, that proposal deals strictly with overload resolution. You still won't be able to define the following:

void F<T>(T arg1) where T : class { }
void F<T>(T arg1) where T : struct { }

And even if they were that doesn't change any of the rest of the argument which is that a syntax already exists which is not appreciably more verbose, is not removed from the declaration and is more capable of describing the breadth of functionality offered by constraints.

@alrz
Copy link
Contributor Author

alrz commented Dec 23, 2015

I was specifically talking about situations when nullables are involved, but as you said, due to the restrictions of inline constraints mixing these two syntaxes doesn't seem like a good idea, and ultimately it doesn't prevent duplication of type parameters.

@alrz alrz closed this as completed Dec 23, 2015
@alrz
Copy link
Contributor Author

alrz commented Jan 11, 2016

@HaloFour Nevertheless, Rust does allow both syntaxes. and uses + for multiple base types (which is not applicable to C#, I guess). But I think that would be good enough to consider.

@alrz alrz reopened this Jan 11, 2016
@HaloFour
Copy link

@alrz Is "Rust does it" worth 200 points? I still don't see what there is to gain by having two syntaxes where neither provides particular functionality, clarity or verbosity benefits over the other.

@alrz
Copy link
Contributor Author

alrz commented Jan 11, 2016

@HaloFour Even in the aforementioned language where is more powerful! for a constraint like where Option<T> : Debug you can't use the inline syntax. Actually, inline constraints are a _limited form_ of where (so don't expect them to provide "more" functionality) but they are concise and self contained in simple cases, IMO.

@dsaf
Copy link

dsaf commented Jan 11, 2016

Not sure if this is a matter of habit, but I find the suggested syntax harder to read.
Concise != more readable.
I was initially overexcited about expression-bodied methods, but after some practical discussions with co-workers we are using it quite sparingly now - sometimes there is just too much stuff going on one line.

@dsaf
Copy link

dsaf commented Jan 11, 2016

Also (feel free to dismiss as irrelevant mental experiment):

--Something I understand, maybe because of habit.
SELECT Name, Value
FROM Sample
WHERE Name = 'Abc' AND Value > 50

vs.

--More concise version I just came up with.
SELECT Name(='Abc'), Value (>50)
FROM Sample

@alrz
Copy link
Contributor Author

alrz commented Jan 12, 2016

@dsaf "sometimes there is just too much stuff going on one line." I think features like expression-bodied methods/lambdas, throw expressions, switch expressions, sequence expressions, etc, all come from one thing: C# is not an expression-based language, and proposed features are trying to fill this gap; to be not too verbose when you just have an expression, but at the same time you might loose consistency. Sometime I feel it would be better to switch to another language instead of shoehorn all these features to C# which is directly influenced by C-family languages, and because of that there are a lot of design problems or features that cannot be easily solved or added because of backward compatibility. As for readability, I agree that this feature could be easily abused but same issue is applied to a lot of other features as well, for example,

Expr Simplify(Expr expr) => expr switch(
    case ...
    case ...
    case ...
    case ...
);

And it doesn't even look like C# anymore! I emphasized, in simple cases, I would prefer inline constraints.

@gafter
Copy link
Member

gafter commented Mar 20, 2017

We are now taking language feature discussion on https://github.com/dotnet/csharplang for C# specific issues, https://github.com/dotnet/vblang for VB-specific features, and https://github.com/dotnet/csharplang for features that affect both languages.

You seem to have moved this to dotnet/csharplang#279

@gafter gafter closed this as completed Mar 20, 2017
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

6 participants