Proposal: Language support for Func & Action #269
Replies: 42 comments
-
Have you tried expression bodied local functions? For quick comparison- Func<int, string> func = a => a.ToString(); // lambda
string func(int a) => a.ToString(); // local function, expression bodied
(int => string) func = a => a.ToString(); // this proposal |
Beta Was this translation helpful? Give feedback.
-
With #150, the delegate types can be inferred without requiring new syntax. |
Beta Was this translation helpful? Give feedback.
-
@gulshan local functions do not serve the same purpose as delegates. |
Beta Was this translation helpful? Give feedback.
-
@YaakovDavis the main purpose of this proposal is to be able to specify named parameters to provide better documentation. The variable declaration syntax is just a natural addition but isn't the primary focus. |
Beta Was this translation helpful? Give feedback.
-
OK, got it. |
Beta Was this translation helpful? Give feedback.
-
My thoughts:
|
Beta Was this translation helpful? Give feedback.
-
@svick thanks for the great feedback.
|
Beta Was this translation helpful? Give feedback.
-
I'm not sure I mind the empty input parameter or empty return parameter given the required outer parentheses which aren't required for lambdas. |
Beta Was this translation helpful? Give feedback.
-
So for Rather than public TValue AddOrUpdate(
TKey key,
Func<TKey, TValue> addValueFactory,
Func<TKey, TValue, TValue> updateValueFactory
) It would be something like the following? public TValue AddOrUpdate(
TKey key,
(TKey key => TValue value) addValueFactory,
(TKey key, TValue oldValue => TValue newValue) updateValueFactory
) |
Beta Was this translation helpful? Give feedback.
-
The tuple feature was pretty explicitly designed so that you could name the values you were returning from a method. So it's pretty clear naming return values has merit. It's true you can't name non-tuple return values, but that seems more like a consequence of C heritage rather than an intentional design choice around which other design choices should be based. |
Beta Was this translation helpful? Give feedback.
-
@benaadams yes, that's a great use case for this feature. I'll add a section to the proposal for existing use cases in the BCL. |
Beta Was this translation helpful? Give feedback.
-
If we did this, i'd far prefer us stick with something much more in line with how lambdas are written, and more in line with how TypeScript does this. i.e. I also not only don't see the value in naming the return type, but i also think it would be extremely out of place as you can't do this anywhere else in the language. It seems like it would be quite weird to have such a one-off facility just for this construct, when everywhere else in the language this facility would not be available. The parentheses placement seems decidedly non-csharpish. For example, instead of:
This reuses actual C# syntactic constructs. The grammar production would simply be:
But i do see a problem you're trying to solve. Namely that now using this type along with a variable name looks quite strange: Maybe a middle ground is:
|
Beta Was this translation helpful? Give feedback.
-
The parentheses placement seems decidedly non-csharpish. For example, instead of:
This reuses actual C# syntactic constructs. The grammar production would simply be:
But i do see a problem you're trying to solve. Namely that now using this type along with a variable name looks quite strange: |
Beta Was this translation helpful? Give feedback.
-
@CyrusNajmabadi Maybe then its worth rethinking whether naming return values makes sense in general? It was worthwhile naming the members of tuples, specifically so that you could return multiple values from a method and have them make sense, so why would should it not be worthwhile more generally? Autocompletion could be a lot nicer if it when storing the value returned by a method it could suggest the exact name the author intended. This is a bit of a tangent though. I think the general feature doesn't require this piece to still be useful. |
Beta Was this translation helpful? Give feedback.
-
Due to the issue @CyrusNajmabadi mentioned I think the outer parentheses should be required. I've created the below table to help determine the syntax for when there are no input or return parameters.
The parentheses around all input parameters gets confusing as users may confuse them with tuples. After looking at it I think I prefer the "Parentheses only for no input parameters" and "Void return parameter". What do you all think? Also what do you think about implicitly supporting multiple return parameters using tuples or do you think it should be explicit using tuple syntax? |
Beta Was this translation helpful? Give feedback.
-
@Grauenwolf My issue with using specific delegate types everywhere is that it's hard to say what the delegate signature is from seeing a signature of a method that accepts it. For example, consider this method signature: public static RegisteredWaitHandle RegisterWaitForSingleObject(
WaitHandle waitObject,
WaitOrTimerCallback callBack,
object state,
uint millisecondsTimeOutInterval,
bool executeOnlyOnce
) Now, can you tell me what parameters does the |
Beta Was this translation helpful? Give feedback.
-
Yea, you do have a point there.
…-----Original Message-----
From: "Petr Onderka" <[email protected]>
Sent: 3/20/2017 4:59 PM
To: "dotnet/csharplang" <[email protected]>
Cc: "Jonathan Allen" <[email protected]>; "Mention" <[email protected]>
Subject: Re: [dotnet/csharplang] Proposal: Language support for Func & Action(#269)
@Grauenwolf My issue with using specific delegate types everywhere is that it's hard to say what the delegate signature is from seeing a signature of a method that accepts it.
For example, consider this method signature:
public static RegisteredWaitHandle RegisterWaitForSingleObject(
WaitHandle waitObject,
WaitOrTimerCallback callBack,
object state,
uint millisecondsTimeOutInterval,
bool executeOnlyOnce
)Now, can you tell me what parameters does the WaitOrTimerCallback delegate take and what is its return type? With Func/Action, that's immediately clear.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
Beta Was this translation helpful? Give feedback.
-
That sounds like a problem that could be resolved through improved tooling. I have the inverse opinion. I prefer to define my own delegate types for any non-trivial case as I find that the raw signature conveys nothing as to what targets of that delegate are supposed to do. With a custom delegate type I can name my arguments something obvious and provide my own XML documentation. |
Beta Was this translation helpful? Give feedback.
-
@HaloFour I agree that that is an issue with |
Beta Was this translation helpful? Give feedback.
-
Feels backwards to me. Like trying to justify the use of tuples everywhere instead of proper structs/classes as data structures. It wouldn't even be beneficial without accompanying tooling work as its not like the IDE automatically pops in the argument names from the delegate definition into your lambda. And even more compiler/tooling changes to consider XML documentation. I get the desire to be more succinct, but I'd rather approach it from formal delegate definitions and work in the other direction. I'm not sure that I have any idea how to do that, though. But I don't particularly feel that writing delegate types is that cumbersome anyway. |
Beta Was this translation helpful? Give feedback.
-
I agree that naming parameters in Func and Action is really important. The problem I have with writing delegate types is figuring out good names for them especially if there are many overloads of a similar concept. Also, and this is less of an issue, I don't like having to define the delegate when it is used in only one place. |
Beta Was this translation helpful? Give feedback.
-
Why
instead of
though? Less tedious as far as adapting the codebase goes because it only barely changes the syntax. Loving the proposal though. |
Beta Was this translation helpful? Give feedback.
-
Well, the |
Beta Was this translation helpful? Give feedback.
-
Hrm i see. The only downside of having this feature implemented this way then would be compatibility to old code then. Let's see how microsoft decides what syntax it will be if it gets implemented. I sure hope it does get implemented in one way or another |
Beta Was this translation helpful? Give feedback.
-
The existing explicit syntax of declaring |
Beta Was this translation helpful? Give feedback.
-
Other than a vague mention of "providing better documentation" I don't see anything in this proposal that actually describes how it would improve the development experience.
How specifically would this proposal address "documentation"? Where would those attributes containing this metadata be used? Also, by binding the syntax to the ubiquitous I honestly just don't see the point. |
Beta Was this translation helpful? Give feedback.
-
Thanks for the thoughts. Due to your feedback I have added sections for considering support for named, optional, and params parameters. Here are the main advantages by adding this feature.
|
Beta Was this translation helpful? Give feedback.
-
Switched the proposal to require parentheses around all input parameters as it works better with optional / default parameters as well as params parameters and better matches lambda syntax. Added open question about whether to support an optional simplified syntax for when there is only one input parameter to not require the surrounding parentheses and if so should it only apply when there is no parameter name associated with the parameter such as (int => string) or should it also apply when there is a parameter name such as (int value => string)? |
Beta Was this translation helpful? Give feedback.
-
Please consider not just parameter naming but also documenation. It would be great to comment a Func field declaration like this: /// <summary>My function</summary>
/// <param name="a">Description of parameter</param>
/// <param name="b">Description of parameter</param>
public Func<int a, int b, int> MyFunc { get; set; } |
Beta Was this translation helpful? Give feedback.
-
Another solution for this, which some may consider hacky, would be #6636. Tuple values can be named - sure, so always use tuples in your
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Similar to what was done for
ValueTuple
in roslyn#347,Func
andAction
should be given language support in order to support naming parameters as well as making them more user friendly, especially for new .NET developers who are less acquainted with them. Named parameters can currently be specified when creating custom delegates but doing so is discouraged as they aren't assignable to the ubiquitousFunc
andAction
. The table below shows what this feature might look like.Action
(() => void)
Action<decimal>
((decimal amount) => void)
Action<int, string>
((int index, string value) => void)
Func<DateTime>
(() => DateTime)
Func<DateTime, int>
((DateTime startDate) => int)
Func<int, int, int>
((int inclusiveMin, int exclusiveMax) => int)
The great thing about this is that existing API's can be updated to provide better documentation of the input parameters of these delegates without breaking compatibility.
Syntax
The general syntax is
((inputParameters) => returnType)
whereinputParameters
are a comma separated list of the input parameters or empty when there are no input parameters.Enclosing parentheses are required
Enclosing parentheses are required to distinguish the alias as a type.
Arrow
The arrow
=>
separates the input parameters from the return type. It was chosen as it mirrors lambda and expression bodied members syntax.No input parameters syntax
The syntax for no input parameters is a set of empty parentheses,
()
. The set of empty parentheses was chosen as it mirrors lambda's syntax for no input parameters.No return parameter syntax
The syntax for no return parameter, e.g.
Action
delegates, isvoid
as this mirrors the existing method syntax.Parameter names
The parameter names are optional and would only be stored at API surface levels in an
Attribute
similar to how tuples work.Alias syntax within methods
Additionally this syntax could also be used within methods to declare variables so the following
could become
Existing BCL Use Cases
To show how ubiquitous
Func
andAction
are in the BCL, when going through the preliminary .NET Standard 2.0 spec there was found around 544 references toFunc
and around 121 references toAction
.LINQ uses
Func
so all of those methods would benefit.Factory parameters would also benefit such as those in
ConcurrentDictionary
'sAddOrUpdate
method which would go from thisto this
Named parameter support
Invoking
Func
s andAction
s using named parameters should be supported such that what is now thiscan now become the following which improves readability.
Using
Invoke
explicitly such as with the null propagation operator should also support named parameters.Optional / Default parameter support
Support for optional / default parameters should be considered through use of attributes at API boundaries. Below is an example that might be considered.
Params parameter support
Support for params parameters should be considered through use of attributes at API boundaries. Below is an example that might be considered.
Open Questions
(int => string)
or should it also apply when there is a parameter name such as(int value => string)
?Updates
Beta Was this translation helpful? Give feedback.
All reactions