Using a lambda expression in reassignment of itself. #2490
-
A while ago I was working on a problem that required me to re-assign a variable to a new lambda expression, but use it's previous definition in the function body. This led to an infinite loop of a function calling itself and eventually a stack overflow. Example code:
After searching for a long time, I accidentally found Clone(), and thought that would never work.
I feel like claiming C# has 'first class functions' is false, they don't act like any other variable. If I use a variable in its redefinition then it(the variable, ) basically gets dereferenced(to the object reference or the underlying value), right? Because it took me a while to figure this out, even with stackoverflow helping, I feel like this should at least be documented, and preferably a compile-time error because I feel like this can be detected(a lambda's function should never refer to 'itself' within)... Assuming this can't be 'fixed'. |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments
-
The capture doesn't enclose the reference, it encloses the variable. When you reassign the variable you are changing the reference so when you dereference after that assignment you are using the new value. This is generally how closures work. |
Beta Was this translation helpful? Give feedback.
-
I'm not completely sure what you're expecting to happen, but I think the problem is not the lambda reassignment itself, but how lambdas capture variables (which in this case happens to be a lambda). Does the following do what you expect: using System;
public class Program
{
public static void Main()
{
Func<int, int> add = (x) => x + 2;
var temp = add;
add = (x) => temp(x) + 2;
Console.WriteLine(add(2));
}
} |
Beta Was this translation helpful? Give feedback.
-
They do. The difference here is that with any other variable, the initialization code is executed immediately (i.e. before the assignment takes place), whereas with delegates and lambdas, it doesn't, because controlling when the code executes is a big part of why they exist.
You might want to open an issue about that on the dotnet/docs repo, ideally including where you think this should be documented.
I can't be a compile-time error, that would be a breaking change. And there is at least one legitimate pattern that relies on this behavior: recursive lambdas. E.g.: Func<int, int> factorial = null;
factorial = n => n <= 1 ? 1 : n * factorial(n-1); This pattern is much less useful now that we have local functions, but that doesn't mean it's okay to break old code that is using it. |
Beta Was this translation helpful? Give feedback.
-
Local function looks like that: static void Main(string[] args)
{
int factorial(int n) => n <= 1 ? 1 : n * factorial(n - 1);
Console.WriteLine(factorial(5));
} |
Beta Was this translation helpful? Give feedback.
I'm not completely sure what you're expecting to happen, but I think the problem is not the lambda reassignment itself, but how lambdas capture variables (which in this case happens to be a lambda).
Does the following do what you expect: