Extend the languages to support the declaration of functions and types in block scope. Local functions would be capable of using captured variables from the enclosing scope.
There have certainly been occasions in the past where I've wished that C# supported nested methods which had automatic access to the variables declared in the outer scope without the need to pass them as parameters.
Even in old BASIC programming one could do something similar with the crude but effective GOSUB command.
As we already have anonymous methods and lambda expressions, it doesn't seem much of a conceptual jump to support 'named' nested methods as well so I'd certainly be in favor of that part of the proposal
However, I don't remember ever wishing that I could declare whole types within a method which, unless they were restricted in some way, would add significant extra complexity to the language.
Are there any particular use cases you think such a feature would address?
Incidentally, I know that Java supports something called 'local classes' but, on the rare occasions I've needed to program in that language, I've never found a use for them.
I think the local function problem is pretty well solved by C#'s closures
void Foo()
{
int myNumber = 42;
Predicate localFunction = (int num) =>{
return num > myNumber
};
...
myArray.Where(localFunction);
}
The local class problem isn't one i encounter often, but sometimes i need a narrowly scoped class to fulfill an interface contract. Aside from interface shims i can't think of any good use cases for local classes that are not solved by C#'s anonymous classes already.
Named nested methods would have one advantage over anonymous methods or lambda expressions in that you wouldn't need to assign them to a compatible delegate before you could invoke them.
As you know, if there's no suitable predefined delegate type in the .NET framework, you have to come up with your own.
On the other hand, you'd need to specify all parameter types because there would be no way that the compiler could infer them.
Anonymous methods also have some restrictions - you can't use params
in the parameter list and you can't capture ref
or out
parameters of the enclosing method. However, it's possible that these same restrictions would also apply to nested methods if the underlying implementation were similar.
you wouldn't need to assign them to a compatible delegate before you could invoke them.
Action
and Func
cover any delegates you'll need (unless you have more than 16 parameters which would be giant code smell). And the syntax for doing it really isn't much different:
Func<int,int,int> foo = ((a,b) =>
{
return a+b;
});
int foo(int a, int b)
{
return a+b;
}
I don't see a benefit here that justifies the complexity.
I do see somewhat of the point of having the local class (and more importantly struct) simply because anonymous classes have readonly fields, but I don't think there are very many algorithms that actually need them. Most of those algorithms would probably be better off with LINQ and immutable types.
@mirhagk: No, unfortunately they don't. Consider ref, out, pointer, or params parameters. Those are _not_ valid generic arguments and therefore Action and Func can't be used.
Although Action
and Func
can deal with most things, they can't deal with ref
or out
parameters as @axel-habermaier just said:
``` C#
using System;
delegate void MyDelegate(ref int a);
class Program
{
static int a = 2;
static void Main()
{
MyMethod();
}
static void MyMethod()
{
MyDelegate d = (ref int b) => ++b;
d(ref a);
Console.WriteLine(a); // 3
}
}
If you tried to replace `MyDelegate` with `Func<int, int>`, then it wouldn't compile.
If nested methods were introduced, I imagine that the above program would look like this if one uses C# 6.0's 'expression bodied' function feature:
``` C#
using System;
class Program
{
static int a = 2;
static void Main()
{
MyMethod();
}
static void MyMethod()
{
void Nested(ref int b) => ++b;
Nested(ref a);
Console.WriteLine(a); // 3
}
}
A bit cleaner, perhaps :)
Okay that makes sense.
If this is to happen, I'd like to see it in tandem with allowing statements at the top level, ie that class/method definitions are basically treated on the same level as statements. That would allow you to define functions outside of a class, allow for this, and allow statements at the top level (which makes it possible to treat C# as a scripting language, mentioned in #98).
@mirhagk I am very wary of allowing top level statements and classless functions in C#. I can see endless abuse in codebases with little benefit to normal programs. I love C# but i don't think scriptifying it makes sense at all, we have many scripting languages that are better suited to the task.
@AlgorithmsAreCool The thing is C# is already used as a scripting language. Scriptcs and LinqPad both offer scripting capabilities, but with some hacks to allow top level statements (linqpad makes you choose expression vs statements vs program and wraps them as appropriate, scriptcs basically allows bare statements).
Classless functions already _essentially_ exist with static classes. The difference between a static class and a namespace with top level functions is basically nothing.
EDIT: Perhaps the C# grammar would support them, but it would be disallowed for most project types. You could only allow it in a "scripting" context.
On my side I would like to add that the local functions are extremely useful in functional programming as recursive functions. We use this approach very often. Actually the underlying construct is more like:
Func
Fib = n => n > 2 ? Fib(n-1) + Fib(n-2) : 1;
To allow recursive calls.
int Fib(int n) => n > 2 ? Fib(n-1) + Fib(n-2) : 1;
or
rec int Fib(int n) => n > 2 ? Fib(n-1) + Fib(n-2) : 1;
looks definitely nicer :).
+1 for this feature - worth implementing even as a simple syntactic sugar.
The one big advantage that local functions would have over delegates is that delegate invocation is much more expensive than a direct method call. Aside the closure capabilities there really isn't a great deal of benefit of that over a regular private static method. In the case of closures would a local function also have the notion of a capture list, like with #117 ?
@HaloFour If we do capture lists like #117 and also local functions #259 we would have to decide if local functions can specify capture lists. That seems messy.
@gafter I don't disagree. Given that local functions will be much more limited in how they can be used as opposed to delegates it's probably not necessary, either. Instead of promoting the variables to fields in a closure state machine they could silently be ref
parameters which the compiler would fill in.
public void Foo() {
int x = 10;
int IncrementBy(int value) {
x += value;
return x;
}
int result = IncrementBy(1);
Debug.Assert(result == x);
}
could be translated into:
private static void Foo_IncrementBy(int value, ref int x) {
x += value;
return x;
}
public void Foo() {
int x = 10;
int result = Foo_IncrementBy(1, ref x);
Debug.Assert(result == x);
}
If the local function would reference members of the containing type then this
could be passed as another hidden parameter.
Additionally, for perf purposes, I'd like to add that invoking the local function always be done with call
instead of callvirt
. It would also be nice if the compiler would detect if the local function would return with the result from itself and automatically perform a tail call.
Capture lists do not sound like a good idea for recursive local functions unless tail calls are aplicable, IMO non-tail-recursion should disable capture lists (if implemented) to avoid wasting stack memory.
I think this would be a very useful feature. This kind of coding can improve organization and reduce namespace pollution. Additionally, If these local functions were permitted to be generic, it would open up some nice scenarios that are not covered by the System.Func
and System.Action
delegate families.
I had a use for this recently. I was writing a function to do some kind of recursive processing on a tree. That recursive function was only necessary inside that method, so I did not want to make a private method on the class and "pollute the namespace." I started doing something like...
Action<TreeNode> processTreeNode = (TreeNode t) => { do stuff and also processTreeNode }
...but the compiler objected to the recursive reference. I had to first insert a...
Action<TreeNode> processTreeNode = null;
This seemed a bit ugly. And then I read blog post somewhere about the dangers of recursive delegates and figured it was safer to just make a private method in the class.
Another thing to be aware of: nested local functions inside a constructor should be able to set readonly
fields.
I ran into this when assigning to a discriminator field. There was a long piece of logic that I extracted from the constructor into a private method, but this of course failed.
``` c#
class Foo
{
private readonly FooType type;
private Bar[] data;
Foo(Bar[] data) {
this.data = data;
AssignType();
}
void AssignType()
{
//use this.data to find out what the type is
}
}
```
Of course, there's a workaround of turning AssignType
a pure static function DetermineType
and calling it this.type = DetermineType(data);
, but this feels less OOPish and more functional.
@orthoxerox Not sure about your last proposal. Should the constructor be able to store the function which changes a readonly field in a delegate and allow calling it afterwards?
@orthoxerox The only way the compiler could do that is if it could emit the IL so that the local function was flattened into the constructor. The CLR does not permit readonly
fields to be set anywhere outside of the local constructor, doing so produces an unverifiable assembly.
If the outer function is an iterator and the inner one isn't, would the inner one be able yield values?
``` vb.net
Iterator Function Example() As IEnumerable(Of Integer)
Function InnerFoo()
Yield 1
End Function
Yield 0
InnerFoo()
End Function
```
The inner method cannot interact in any control-flow way with the enclosing method. It cannot return from it, goto any of its labels, break from its loops, yield from its, etc. In this way nested local (named) functions to not differ from anonymous functions.
How's are they different?
Inner Function
Function Foo()
Function Bar()
' blah
End Function
Bar()
End Function
and Function Lambda.
vb.net
Function Foo()
Dim Bar = Function()
'blah
End Function
Bar
End Function
@AdamSpeight2008
Recursion should work for the lambda, I just need to declare the type of the Func.
Dim Fib1 = Function(arg As Integer) As Integer
' Error: cannot reference Fib2 before its declaration
Return If(arg <= 1, 1, Fib2(arg - 1) + Fib2(arg - 2))
End Function
Dim Fib2 = Function(arg As Integer) As Integer
Return If(arg <= 1, 1, Fib1(arg - 1) + Fib1(arg - 2))
End Function
Dim Fib1, Fib2 As Func(Of Integer, Integer)
Fib1 = Function(arg As Integer) As Integer
' Warning BC42104: Variable 'Fib2' is used before it has been assigned a value. A null reference exception could result at runtime.
Return If(arg <= 1, 1, Fib2(arg - 1) + Fib2(arg - 2))
End Function
Fib2 = Function(arg As Integer) As Integer
Return If(arg <= 1, 1, Fib1(arg - 1) + Fib1(arg - 2))
End Function
@gafter
Well you could always do the following, but ew: :wink:
Dim Fib2 As Func(Of Integer, Integer) = Nothing
Dim Fib1 = Function(arg As Integer) As Integer
Return If(arg <= 1, 1, Fib2(arg - 1) + Fib2(arg - 2))
End Function
Fib2 = Function(arg As Integer) As Integer
Return If(arg <= 1, 1, Fib1(arg - 1) + Fib1(arg - 2))
End Function
You mentioned the memory allocations but not the delegate invocation. That should improve performance considerably, no?
Yes, the direct method invocation is indeed faster than a delegate invocation.
I was think more of
vb.net
Dim Fib As Func(Of Integer, Integer) = Function(arg As Integer) As Integer
Return If(arg <= 1, 1, Fib(arg - 1) + Fib(arg - 2))
End Function
@AdamSpeight2008 That is more of a benefit for C# where that would result in a compile-time error unless you predefine the delegate variable:
// error: use of unassigned variable fib
Func<int, int> fib = i => (i <= 1 ? 1 : fib(i - 1) + fib(i - 2));
// legal
Func<int, int> fib = null;
fib = i => (i <= 1 ? 1 : fib(i - 1) + fib(i - 2));
The lack of a delegate and the performance overhead that brings to the equation is difference enough for me.
consistent with methods
to be consistent also with local declarations I'd say var
be allowed as return type
let let = e1;
var var = e2;
var f() => e3;
I don't know how "forward reference" would be _more_ useful.
to be consistent also with local declarations I'd say
var
be allowed as return type
... and restricted to cases where the body does not use itself (recursively) or any later-declared function? And cannot be used before it is declared? Which means mutual recursion would not be supported?
@alrz here's a bad example, but this is part of the trouble with having both forward reference local functions and var
return type local functions:
public void Foo()
{
var Bar() => Baz();
var Baz() => Bar();
}
As to why forward reference is good, consider this (vaguely copied from a js implementation I have laying around; probably doesn't compile on @gafter's branch or work correctly) implementation of joint binary search via mutual tail recursion. Supposing that method was private (you shouldn't be exposing SortedList<T, V>
publicly anyway) on a partial class, you may not want to expose those helpers to even other members of that class. So today you would be stuck with shoving it off as private members of some implementation class. Unfortunately that is one more thing to name (back to the hardest problem for a developer...).
_Maybe_ a restricted allowance of var
on some subset of all possible local functions could be desirable. "Forward reference" is almost definitely desirable. Can we decide today what those restrictions are without knowing exactly how forward reference will behave (it is probably at least what @gafter just said)? Is that still a worthwhile subset? Would coming up with a specification today on how you could use var
functions limit the specification of how forward reference could be done?
I would much rather see a working solution that provides forward reference over one that allows var in some spot that prevents forward reference from happening somehow.
@gafter
... and restricted to cases where the body does not use itself (recursively) or any later-declared function? And cannot be used before it is declared? Which means mutual recursion would not be supported?
Why should it be restricted? In F# return types can be inferred, although, it only permits recursion with a specific keyword rec
and and
for mutual recursion. But uses let
for both variable and function declarations. Here is my question, don't you think that local functions should be distinguishable from member methods?
@bbarry
public void Foo() { var Bar() => Baz(); var Baz() => Bar(); }
Are you trying to stack overflow? Yeah that is one baaad example. otherwise I don't see where the "trouble" is.
I just realized in a code like the gist you've mentioned, it's hard to know that the defined method is a local function or member method! var
might help.
public void Foo()
{
var Bar() => Baz();
var Baz() => Bar();
}
Foo()
that returns. Bar()
and Baz()
are never called. Thus no stack overflow.var
?"Foo()
did call Bar()
, that code might not be a stack overflow anyway because it is mutual tail recursion. I think it would be nice if that code warned twice: "Unreachable local method declared." for both Bar()
and Baz()
(indeed the latest sources do in similar cases that are not recursive according to the tests).
It should also complain about var
being ambiguous ("error CS7019: Type of 'Bar()' cannot be inferred since its initializer directly or indirectly refers to the definition." according to tests in the latest sources).
As to knowing whether the defined method is a local function or member method, yes that is admittedly a potential source of confusion for users. It is even worse if local functions are nested inside each other (var
doesn't help at all). Here's that same gist, now with no forward references.
Can we also have Local expression bodied Properties like-
float m;
float c;
var E => m*c*c;
//assign values for m and c later and E will be evaluated to mc^2 when called/used
@gulshan,
You could use an anonymous delegate or lambda for that purpose:
Func<float> E = () => m*c*c;
That would create a closure over m
and c
, though.
I think the real question here is: do you have a compelling use case for local properties (regardless of how they are expressed)? What could you do with them that you couldn't do with local functions?
We have local functions lined up for C# 7 already done in the future branch, so I'm going to close this as done. If someone wants local type declarations, please open a separate issue for that.
Good one...
This should be tagged with New Language Feature - Local Functions
.
This issue is so weird because _there's no actual proposal here_, just a "hey, let's do local functions!" - What's the impetus for the proposal? What would the semantics be? What would the advantage(s) be over the alternatives, such as simply inferring that var square = (int x) => x * x
has type Func<int, int>
? And then _bam_, @gafter who opened the issue announces it's done.
It's good to notify the community you're going to do a feature, but this isn't enough information for an intelligent discussion.
Note: This was opened a year ago. And closed a month ago. So it's probably too late for any further discussions. In fact, you're just saying "why didn't anybody tell me?"
In case it will help anyone, I found _slightly_ more information in "local-functions.md". See also #2930
Edit: Aha! found the real proposal at #3911.
I suggest another syntax to local functions to be unnested https://github.com/dotnet/csharplang/issues/1329
Most helpful comment
Note: This was opened a year ago. And closed a month ago. So it's probably too late for any further discussions. In fact, you're just saying "why didn't anybody tell me?"