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

Champion "Lambda discard parameters" (VS 16.8, .NET 5) #111

Open
2 of 5 tasks
Tracked by #829
gafter opened this issue Feb 15, 2017 · 97 comments
Open
2 of 5 tasks
Tracked by #829

Champion "Lambda discard parameters" (VS 16.8, .NET 5) #111

gafter opened this issue Feb 15, 2017 · 97 comments
Assignees
Labels
Implemented Needs ECMA Spec This feature has been implemented in C#, but still needs to be merged into the ECMA specification Proposal champion Proposal Smallish Feature
Milestone

Comments

@gafter
Copy link
Member

gafter commented Feb 15, 2017

Examples: (_, _) => 1, (int _, string _) => 1, void local(int _, int _) ...

See

@gafter gafter self-assigned this Feb 15, 2017
@gafter gafter changed the title Discard for lambda parameters (_, _) => Champion Discard for lambda parameters (_, _) => Feb 15, 2017
@DavidArno
Copy link

DavidArno commented Feb 20, 2017

Please, please, please could this apply to methods too? Sometimes, the parameters must be there, but just aren't used. By way of example, this Unit type has to have both R# and CA1801 suppressions for the operator overloads:

public static bool operator ==(Unit u1, Unit u2) => true;
public static bool operator !=(Unit u1, Unit u2) => false;

Being able to write those with discards to reassure those checks that the parameters must be there, but aren't used, would clean up such code nicely:

public static bool operator ==(Unit _, Unit _) => true;
public static bool operator !=(Unit _, Unit _) => false;

@jnm2
Copy link
Contributor

jnm2 commented Feb 20, 2017

@DavidArno I believe the .NET Framework design guidelines have always been to use the names left and right specifically. https://msdn.microsoft.com/en-us/library/ms229004.aspx

@DavidArno
Copy link

@jnm2,

Oh, I didn't know that. I'll give that a try to see if it makes the compiler happier. Would still like to be able to use discards though.

@gafter gafter changed the title Champion Discard for lambda parameters (_, _) => Champion "Discard for lambda parameters (_, _) =>" Feb 21, 2017
@gafter gafter added this to the 7.1 candidate milestone Feb 21, 2017
@acple
Copy link

acple commented Feb 22, 2017

this proposal also permit in nested lambda expression _ => _ => true ?

Enumerable.Range(0, 3)
    .SelectMany(_ => Enumerable.Range(0, 3)
        .SelectMany(_ => Enumerable.Range(0, 3))); // i don't need parameters in nested

@gafter
Copy link
Member Author

gafter commented Feb 22, 2017

@acple Possibly not, unless we also do something like dotnet/roslyn#15793 at the same time.

@acple
Copy link

acple commented Feb 22, 2017

@gafter Thank you. in my opinion, lambda parameter shadowing is a bit dangerous but, discarded parameters are safe to overcover. so that should support only with discarded parameters. is this shadowing issue? or discarding issue?

@gafter
Copy link
Member Author

gafter commented Feb 22, 2017

@acple This is a shadowing issue. When there is only one lambda parameter named _, it is a normal identifier that declares a parameter. It must be so for backward compatibility.

@acple
Copy link

acple commented Feb 23, 2017

@gafter okay I understand. desire the identifier definition rules will be relaxed partially... thanks for the information! 😄

@MovGP0
Copy link

MovGP0 commented Feb 27, 2017

there should also be a compiler warning/error when using an _ named variable:

items.ForEach(_ => Console.WriteLine(_));

@DavidArno
Copy link

@MovGP0,

Unfortunately, that would be a breaking change. An analyzer could be added to this affect though.

@jnm2
Copy link
Contributor

jnm2 commented Feb 27, 2017

I think use of a single _-named parameter should be encouraged! I don't intend to use it any less.

@DavidArno
Copy link

@jnm2,

I disagree. If I was going to use the parameter in the lambda body, then I'd use eg x. I think _ should only be used when the parameter(s) aren't used in the body, ie as discards.

I appreciate that's personal opinion though.

@jnm2
Copy link
Contributor

jnm2 commented Feb 27, 2017

@DavidArno Exactly, it is personal opinion. My rationale is that I'd like to not have to specify a parameter at all, so until I can do .Where(@.Foo != null) or similar, _ => _.Foo seems to be the nearest I can get to not specifying a parameter name, readability-wise.

@MovGP0
Copy link

MovGP0 commented Feb 27, 2017

I disagree that it is a personal opinion, since it is a common convention in functional languages that _ is a 'throwaway' value that is not to be used. It confuses developers who are used to the convention.

Thankfully it is quite simple to give it a proper name.

@gafter
Copy link
Member Author

gafter commented Feb 28, 2017

I disagree that it is a personal opinion...

Oh, the irony.

@jcouv
Copy link
Member

jcouv commented Oct 28, 2019

From discussion today in LDM, we're going to keep the feature narrow for now (only support it for lambdas and anonymous methods as initially proposed).

@YairHalberstadt
Copy link
Contributor

Can I ask why doing this for all methods is more complicated?

@jcouv
Copy link
Member

jcouv commented Oct 28, 2019

@YairHalberstadt It's not a question of implementation cost, but trade-off between language complexity and benefit to users. Methods are part of an API (they can be invoked, whereas lambdas can only be converted to delegates, so the parameters names are never accessible to callers).
It is possible that discards would be generalized to methods later on, depending on feedback. More notes will be posted for today's meeting.

@atifaziz
Copy link

lambdas can only be converted to delegates, so the parameters names are never accessible to callers

Aren't they available through reflection? I rely on that in a library.

@jnm2
Copy link
Contributor

jnm2 commented Oct 29, 2019

@jcouv Can local functions be included? They are often converted to delegates.

@DavidArno
Copy link

DavidArno commented Oct 29, 2019

@jcouv,

You raise a good point re methods that I hadn't thought of. Consider the following code example,

public interface I 
{
    public void M(int a, int b);
}

public class C : I
{
    public void M(int _, int _) {}
}

public static class P
{
    private static void Main()
    {
        I i = new C();
        C c = new C();
        
        i.M(a:1, b:1);
        c.M(_:1, _:1);
    }
}

In C, I've used discards for M as I don't use the parameters. This makes sense to me: why name a parameter that isn't used?

But if I'm a developer working in a team that insists on named parameters (not sure if such a team exists, but thinking worst case here), then that line c.M(_:1, _:1); can't compile as I've trying to name those discards.

My motivation for wanting discards in methods (and operators, though I'm just lumping them in with methods here) is for situations such as when defining event handlers, operators and the like. In these situations, I have to specify the parameters to meet the contract. But if I then don't use those parameters, the compiler IDE complains with an IDE0060 message. So I want to use discards there to keep the compiler IDE happy.

But maybe this is the wrong solution to the problem. If a method must have a parameter to meet the contract, but that parameter isn't used, the compiler IDE puts the developer in the impossible situation difficult position of having to ignore that message. So maybe the simpler solution is to just not warn on those unused parameters when they are mandatory?

@Joe4evr
Copy link
Contributor

Joe4evr commented Oct 29, 2019

@DavidArno I think in the case you present, that should likely be a compiler error since C.M is an implicit implementation of I.M and there need to be unambiguous identifiers for the first and second parameter because M is a public API regardless of whether or not C happens to be followed by : I.

But, I think explicit implementations maybe could allow discarded parameters since those methods are only callable through the interface signature. A potential (but perhaps unlikely) addition to that would be to also allow it in method overrides provided that the overriding class is less visible than the base class? Idk, it's obviously quite a complicated scenario.

@HaloFour
Copy link
Contributor

@DavidArno

So maybe the simpler solution is to just not warn on those unused parameters?

I agree, the better approach would seem to have the analyzers not generate diagnostics for unused parameters that are required to meet the contract, such as for implicit implementation of an interface member or for overloaded operators, etc.

In general I'm a little squeamish about extending _ to all parameters. Lambdas and local functions are at least internal implementation details, but public methods are more likely to be walked via reflection and I have concerns over how such parameters would be interpreted by tooling.

@Logerfo
Copy link
Contributor

Logerfo commented Oct 29, 2019

Including local functions sounds perfect. Even private methods have their reflection concerns.

@jcouv jcouv modified the milestones: Any Time, 9.0 candidate Dec 16, 2019
@louthy
Copy link

louthy commented Mar 17, 2020

Will this also work for LINQ (as LINQ gets compiles down to lambdas)? For those of us that use LINQ for more than just querying an enumerating this would be an absolute godsend.

It would reduce the calls for an extension to the LINQ grammar. For example, I'm just working on a new IO monad:

static SIO<Runtime, ConsoleKeyInfo> ShowError(Error err) =>
    from _1 in setColor(Red)
    from _2 in writeLine(err.Message)
    from _3 in writeLine()
    from _4 in writeLine(StackTrace(err))
    from _5 in setColor(White)
    from k in readKey
    select k;

I would prefer to do this:

static SIO<Runtime, ConsoleKeyInfo> ShowError(Error err) =>
    do setColor(Red)
    do writeLine(err.Message)
    do writeLine()
    do writeLine(StackTrace(err))
    do setColor(White)
    from k in readKey
    select k;

But would be fine with just this (as a stopgap until the day that LINQ gets some much needed love):

static SIO<Runtime, ConsoleKeyInfo> ShowError(Error err) =>
    from _ in setColor(Red)
    from _ in writeLine(err.Message)
    from _ in writeLine()
    from _ in writeLine(StackTrace(err))
    from _ in setColor(White)
    from k in readKey
    select k;

@ChaseFlorell
Copy link

I asked this question on Twitter, but figured I should bring it over here
https://twitter.com/ChaseFlorell/status/1286159277451653121

Question, for those insane folks who use _ => {} and expect a value from it, is this a breaking change?

Is there a different character you can use that would have otherwise been invalid pre C# 9? . => {} perhaps?

@jnm2
Copy link
Contributor

jnm2 commented Jul 28, 2020

@ChaseFlorell No, it is not a breaking change. This feature only kicks in when at least two parameters are discarded.

@jcouv jcouv changed the title Champion "Lambda discard parameters" Champion "Lambda discard parameters" (VS 16.8, .NET 5) Sep 1, 2020
@MadsTorgersen MadsTorgersen modified the milestones: 9.0 candidate, 9.0 Sep 9, 2020
@CyrusNajmabadi
Copy link
Member

@jcouv do we have an existing issue covering multiple discards for methods, or multiple discards for locals?

@jcouv
Copy link
Member

jcouv commented Oct 12, 2020

@CyrusNajmabadi Tracked by #2180

@CyrusNajmabadi
Copy link
Member

Thanks!

@333fred 333fred added the Implemented Needs ECMA Spec This feature has been implemented in C#, but still needs to be merged into the ECMA specification label Oct 16, 2020
@jcouv jcouv moved this to Done Umbrellas in Compiler: Julien's umbrellas Jun 3, 2024
@dotnet dotnet locked and limited conversation to collaborators Nov 19, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Implemented Needs ECMA Spec This feature has been implemented in C#, but still needs to be merged into the ECMA specification Proposal champion Proposal Smallish Feature
Projects
Status: Done Umbrellas
Development

No branches or pull requests