[Proposal] Implicit lambda parameter and trailing lambda in C# #6122
Replies: 9 comments 15 replies
-
Very similar to #1151 Kotlin and Scala both have a flavor of this feature (or set of features which can be combined to achieve this kind of syntax) and it can be useful to define DSL-like syntax. Although, IMO, Scala takes it a bit too far as you can omit the dot operator (sometimes) and you can omit the parenthesis (sometimes). |
Beta Was this translation helpful? Give feedback.
-
#1151 went too far imo, it retargets what |
Beta Was this translation helpful? Give feedback.
-
@ronnygunawan wrll, your suggestion retargets |
Beta Was this translation helpful? Give feedback.
-
@orthoxerox well, I think both retargeting Kotlin and Java solve the |
Beta Was this translation helpful? Give feedback.
-
Is this on the consideration list for C# 11 / C# 12? The RemObjects C# compiler supports this Trailing Closures feature quite nicely, and Swift and Kotlin have supported this from the get-go (5+ years now). Would be great to see C# catch up to this modern must-have that can unlock all sorts of DSL-style APIs. NOTE: Swift has since extended to support multiple trailing closures (full feature spec), thus directly supporting scenarios such as the example given above: STRETCH: Not sure if compatible with C# named parameters overall, but being able to optionally specify those as well would make it these much more readable/useful as well. Some very useful real-world examples from functional-style utilities: //
// EXAMPLE 1
//
// current lambdas
DateTimeOffset rangeEndDate = endDateTime?.IfNotNull( endDateTime_ =>
endDateTime_.StartOfDay() - ((crossesMidnight) ? 1 : 0).Days(),
otherwise: () => startDateTime.StartOfDay() );
// with trailing lambda
DateTimeOffset rangeEndDate = endDateTime?.IfNotNull(
value.StartOfDay() - ((crossesMidnight) ? 1 : 0).Days()
){ startDateTime.StartOfDay() };
// with multiple & named trailing lambdas (STRETCH)
DateTimeOffset rangeEndDate = endDateTime?.IfNotNull {
value.StartOfDay() - ((crossesMidnight) ? 1 : 0).Days()
}, otherwise: { startDateTime.StartOfDay() };
//
// EXAMPLE 2
//
// current lambdas
GeoCoordinate? coordinate = Try.CatchingAsNull<ArgumentOutOfRangeException, GeoCoordinate?>( () =>
new GeoCoordinate( place.geoCoordinate.latitude, place.geoCoordinate.longitude ),
@catch: x => { // NOTE: indentation hack to make it "feel" like a trailing lambda
Log.LogError( $"Geo-coordinate for content #{attraction.id} out of bounds: ({place.geoCoordinate.latitude}, {place.geoCoordinate.longitude}). Error: {x.Message}" );
});
// with trailing lambda
GeoCoordinate? coordinate = Try.CatchingAsNull<ArgumentOutOfRangeException, GeoCoordinate?>(
new GeoCoordinate( place.geoCoordinate.latitude, place.geoCoordinate.longitude )
){ // NOTE: no indentation hack. Trailing lambdas naturally style back to the containing statement's indentation
Log.LogError( $"Geo-coordinate for content #{place.id} out of bounds: ({place.geoCoordinate.latitude}, {place.geoCoordinate.longitude}). Error: {value.Message}" );
};
// with multiple & named trailing lambdas (STRETCH)
GeoCoordinate? coordinate = Try.CatchingAsNull<ArgumentOutOfRangeException, GeoCoordinate?>{
new GeoCoordinate( place.geoCoordinate.latitude, place.geoCoordinate.longitude )
}, @catch: x => {
Log.LogError( $"Geo-coordinate for content #{attraction.id} out of bounds: ({place.geoCoordinate.latitude}, {place.geoCoordinate.longitude}). Error: {x.Message}" );
}; |
Beta Was this translation helpful? Give feedback.
-
This could be very useful to convert code in fluent style into something easier to read and write. For example this FluentQuery
.ConnectionString("connectionString")
.StoredProcedure("LoadData")
.Parameters(c=>c
.AddParameter("ParName1", "ParValue1")
.AddParameter("ParName2", "ParValue3")
.AddParameter("ParName3", "ParValue3")
)
.Execute(); into this FluentQuery
.ConnectionString("connectionString")
.StoredProcedure("LoadData")
.Parameters
{
AddParameter("ParName1", "ParValue1")
AddParameter("ParName2", "ParValue3")
AddParameter("ParName3", "ParValue3")
}
.Execute(); or even this FluentQuery
{
ConnectionString("connectionString")
StoredProcedure("LoadData")
Parameters
{
AddParameter("ParName1", "ParValue1")
AddParameter("ParName2", "ParValue3")
AddParameter("ParName3", "ParValue3")
}
Execute()
}; It seems to me it looks much easier to write and read. |
Beta Was this translation helpful? Give feedback.
-
Would allow for much cleaner use of Parallel.ForEach and several AOP-like patterns, too. I support this. |
Beta Was this translation helpful? Give feedback.
-
I write a lot of code in Kotlin, where this syntax exists - and this is a game changer.
|
Beta Was this translation helpful? Give feedback.
-
So, is this conaidered for upcoming c# releases? |
Beta Was this translation helpful? Give feedback.
-
Implicit lambda parameter:
() =>
when lambda has no parameter.arg1 =>
when lambda has only one parameter.() =>
andarg1 =>
, then implicit parameter can't be used.value
keyword.async
can be placed before the opening brace.this
is unaffected. It still refers to enclosing type.Trailing lambda:
Examples:
Functions
Actions
Method with multiple lambda
Get creative with trailing lambda
Beta Was this translation helpful? Give feedback.
All reactions