Open WhiteBlackGoose opened 2 years ago
Some additions. If you want to, you can merge in some of them to the op., I don't want to edit your issue without you knowing it.
Lambda functions (or anonymous functions) are functions or methods that have no statically resolved name, they are usually bound to a local variable or passed as an argument straight away. Their primary use in the .NET ecosystem is passing around single-use methods, like for configuration, factories or inside LINQ methods. They are also usually the primary way to write closures.
In addition to being very concise and readable, I believe it should not diverge from regular functions too much. C# started out with a very different mechanism, but lambdas are slowly being merged together with regular methods in terms of functionality. As we have been suggested, we might be best off by shaving our original functions to get something more compact.
As I've mentioned before, a large percentage of lambdas (mostly in LINQ) create a lot more noise around them than what they should be. Examples:
var ys = xy.Select(x => x.ToFoo()); // Calling a member function on each requires a full lambda
var zs = ns.Select(x => x + 1); // Incrementing each number requires a lambda
var ws = ns.Select(x => Compute(x, 1)); // Just because I have another constant parameter, I need a lambda
You might say that currying or partial application can solve this, and yes, they do somewhat. But a more general feature could be to promote expressions with implicitly defined lambda parameters to be lambda expressions themselves. The previous examples with a made-up syntax, where $n
means the nth lambda parameter:
var ys = xy.Select($0.ToFoo());
var zs = ns.Select($0 + 1);
var ws = ns.Select(Compute($0, 1));
This is more general in the sense, that it even works with multiple arguments and they can be reordered in any way.
Note that there is still a big, unresolved problem with this idea! Namely, it is unclear what subexpressions to promote to lambdas, where the "boundaries" are. This will likely require some separator characters, like in Swift or Kotlin.
C# started out with a very different mechanism, but lambdas are slowly being merged together with regular methods in terms of functionality.
I believe that a major reason that they were treated so differently is that the main driver of getting them added to the language was not just LINQ but LINQ-to-SQL and Entity Framework; and L2S/EF required expression trees, which needed to be restricted to a subset of the full language to prevent L2S/EF from needing to create a full compiler to be able to convert the lambda into SQL.
Expression trees were (and are) a powerful addition to the language, but being restricted by their intended use case makes them much less useful than they could be. They also have not kept up well with the evolution of the language, likely to keep providers from needing to constantly play catch up.
where $n means the nth lambda parameter
it
is so useful because it is unambiguous in single-parameter lambdas, which are extremely common. If I'm e.g. iterating a collection, it is obvious what it
refers to. I worry that as soon as you have more than one parameter, referring to them anonymously will make the code much less readable.
This will likely require some separator characters, like in Swift or Kotlin.
Some examples would be useful here.
Some examples would be useful here.
So my fear is that simply something like ns.Foo($0 + 1)
can be promoted to lambda in so many ways:
ns.Foo((x => x) + 1)
ns.Foo(x => x + 1)
x => ns.Foo(x + 1)
A separator character, like { }
could somewhat disambiguate this: ns.Foo({ $0 + 1 })
but then we are not much better than just having the arrow syntax instead imo.
(Inserted LPeter's comment)
I'd like to add an idea for syntax, which might not be popular, it is literally just getting rid of the name from the regular function syntax.
C#:
() => 5
a => 5
(int a) => 5
(int a, string b) => 5
... => { return 5; }
Fresh:
func() = 5
func(a) = 5
func(a: int32) = 5
func(a: int32, b: string) = 5
func(...) { return 5; }
I'm not sure what we should do with the return type here, optionally we could allow the user to specify it the usual way. Allowing inference here implicitly might be a good idea.
I think keeping your own type would be convenient if you let unit
or void
be usable as a type parameter. This is pretty much the reason the distinction between Action
and Func
exists in my opinion.
Jump to
Overview
Lambda functions (or anonymous functions) are functions or methods that have no statically resolved name, they are usually bound to a local variable or passed as an argument straight away. Their primary use in the .NET ecosystem is passing around single-use methods, like for configuration, factories or inside LINQ methods. They are also usually the primary way to write closures.
Requirements towards the feature
In addition to being very concise and readable, I believe it should not diverge from regular functions too much. C# started out with a very different mechanism, but lambdas are slowly being merged together with regular methods in terms of functionality. As we have been suggested, we might be best off by shaving our original functions to get something more compact.
Syntax
Requirements
It should be very concise and readable.
C# way
Definition:
Type declaration:
Kotlin way
Definition:
Type declaration:
Implicit lambdas
As I've mentioned before, a large percentage of lambdas (mostly in LINQ) create a lot more noise around them than what they should be. Examples:
You might say that currying or partial application can solve this, and yes, they do somewhat. But a more general feature could be to promote expressions with implicitly defined lambda parameters to be lambda expressions themselves. The previous examples with a made-up syntax, where
$n
means the nth lambda parameter:This is more general in the sense, that it even works with multiple arguments and they can be reordered in any way.
Note that there is still a big, unresolved problem with this idea! Namely, it is unclear what subexpressions to promote to lambdas, where the "boundaries" are. This will likely require some separator characters, like in Swift or Kotlin.
Wip...
Internals
There are three ways
Func
andAction
Using .NET's
Func
andAction
Pros:
Cons:
Func
andAction
, whereas it'd be nice to have oneCreating our own type similar to how it does F
Pros:
Cons:
Creating our own interface to allow the user to manually implement it
Pros:
Cons: