Edit: I've broken the proposal out into an answer, but left the examples where they are. Please do not consider a vote for this question to constitute support for the proposal
Apologies in advance, this is long, and may read somewhat like a rant, but I've been holding off asking about this for ages (and reduced my PPCG activity as a result of the increased prevalence of certain classes of answers). I'm sure many will expect that this is a dupe of something more general (another reason I've been slow to write this up) but I can't find any fitting questions or satisfactory answers.
Note: this is a problem common to multiple languages, I'm asking about C# specifically to avoid this being unanswerable because it's too broad, and because I know and love (golfing) C#. From what Java I know, this is probably directly applicable. It's also a 'common case' of what I consider a wider broader concern.
Note: the answer here expresses some of my sentiment, but not quite all of it.
Today I was looking at this question: Output integers in negative order, increase the maximum integer everytime
There are 2 C# answers, and frankly I don't like either of them, but I gave one of them a hard time and not the other, because I think that more people would agree with me that it is perhaps not so much in the spirit of things. I've neither up-voted nor down-voted either, because I'm not happy with this issue. Having thought about it a little more, I've concluded that they are both as bad as each other, and as such we seem to be holding a double standard at the moment.
One answer (https://codegolf.stackexchange.com/a/103672/26981) is of a form which we see all the time in C# answer these days, it's the code for an anonymous lambda.
The other (https://codegolf.stackexchange.com/a/103692/26981) is also a form of anonymous lambda, only this one is recursive, and as such the lambda must be defined and assigned before it can be assigned.
Neither of these compile by themselves. To get them to run, you have to wrap them up somehow. Any modern C#er will know how to do this, but while the first can remain anonymous, the other has to have a name, and this is why it feels a bit odd allowing it, when we are assuming some declaration exists for it (even if the name is indicated in the code submission). Instead of doing this (as for the first answer):
Console.WriteLine(((Func<int, int>)(/*code here*/))(1));
We have to do this (because it's recursive) for the second:
Func<int,int>z=null;/*code here (starting with z=)*/;Console.WriteLine(z(1));
The above assuming a using System;
directive in scope
While not long ago I would have let the first slip, but complain about the second (meta seems to agree: https://codegolf.meta.stackexchange.com/a/10361/26981), I don't now think there is a meaningful difference. My complaint falls into two parts:
Complaint Half A
The code provided does not have familiar and well defined boilerplate to be packed around it. All over PPCG we see people saying that it's OK for C# and Java to omit the (required for csc
) class definitions and just provide a function, because we all know what it is, and it just adds bytes (I would respond "who cares then?" but that's just me), but that is not the case here, because these lambdas lack type info, and as such there is a very large space of boiler plate to inspect. This is my "I think this is the spirit of PPCG" concern.
Complaint Half B
Even if we say "well, a human can work out what types it needs to be, so it's really just boiler plate" then we still have a big problem, something along the lines of C# is statically typed and takes types into consideration during compilation, a consequence of which is that there can be multiple types we can assign a delegate to which produce different behaviour. Consider a challenge "double an integer", and the answer (6 bytes: a=>a+a
):
Console.WriteLine(((Func<int, int>)(a=>a+a))(1)); // returns 2
Console.WriteLine(((Func<string, string>)(a=>a+a))("1")); returns "11"
If the question spec was strong enough, and specified that input must be a numerically typed integer, then we could say "well, the first one is correct, the second one is wrong, but this never happens because 'numerically typed' doesn't really mean anything, and certainly doesn't make sense in a whole host of languages. It's pretty standard to see a question with answers that take input from argv (i.e. a string) and others which take it as input to a method/function/block_of_code/callable_expression/whatever which either demands it be of a 'numeric type', or assumes it be of a 'numeric type' (which is fine in Python (for example) where they don't have the luxury of a static type system).
Given that the spec can't tell us the type, and that the code can't tell us the type, this should not be a valid submission, because it is ambiguous beyond just needing boilerplate. This doesn't preclude allowing functions, because a function definition is self-contained, and either it can be stuffed into a class (where it will compile, carry unambiguous behaviour, and can be called), or it cannot (it won't compile, e.g. name conflicts).
Note: strictly the behaviour of a self-contained method is still open to external influence, even if it compiles fine in minimal boiler plate (unsafe code can ruin your day, or aliasing/replacing types for example) but requiring such behaviour would be obvious and blatantly not in the spirit of things
Summary
There can be no clear-cut objective answer to "what is a function", because it would have to be per-language and such.
Note that I'm not just being miserable here and trying to make C# harder to golf, I think that these are real issues which must be addressed, because they give some C# answers an unfair advantage if we make such allowances, and reduces opportunities for golfing (who cares if all C# answers are a minimum of 26 bytes? they provide utility and ignoring them is irrational).
The actual question...
How would people like to deal with this concern. I'm interested in responses that answer the following:
How do we define a valid submission (must cover the above concerns, either resolving or presenting a case for their insignificance) in C# (though comments regarding other languages with similar concerns are welcome)?
How and to what extent do we enfore any new rulings?
Do we except existing answers?
I'll ignore here the issues of functions needing using
directives, and library references, etc.
Examples (for reference in any discussion, feel free to add more...)
Example A: something which I think the community currently would think is fine (source: https://codegolf.stackexchange.com/a/103672/26981)
n=>{var s="";for(int i=0,j;i<n;)for(j=++i;j>0;)s+=j--;return s;}
Example B: something which I think the community currently isn't sure about, but would err on the side of rejection (source: https://codegolf.stackexchange.com/a/103692/26981)
f=n=>{int x=n;var r="";while(n>0)r+=n--;return x>0?f(x-1)+r:"";};
Example C: classic 'test' code for Example A, and what I would consider a good (i.e. by my proposal valid) version of Example A (note semi-colon at end also)
System.Func<int,string>q=n=>{var s="";for(int i=0,j;i<n;)for(j=++i;j>0;)s+=j--;return s;};
Example D: classic 'test' code for Example B, and what I would consider a good (i.e. by my proposal valid) version of Example B (note semi-colon at end also)
System.Func<int,string>f=null;f=n=>{int x=n;var r="";while(n>0)r+=n--;return x>0?f(x-1)+r:"";};
Example E: boiler plate we can assume for something going into a class
/*usings here perhaps?*/
class LONGCLASSNAMETHATWONTCLASH
{
/*submission here*/
/*calling code (in a method or w/e)*/
}
Example F: boiler plate we can assume for something going into a method
class ANOTHERLONGCLASSNAMETHATWONTCLASH
{
void LONGMETHODNAMETHATWONTCLASH()
{
/*submission code*/
/* calling code */
}
}
Example G: what I would consider a good (i.e. by my proposal valid) version of Example A which requires direct calling
(System.Func<int,string>)(n=>{var s="";for(int i=0,j;i<n;)for(j=++i;j>0;)s+=j--;return s;})
Example H: boiler plate for calling an anonymous well-typed method (e.g. Example G)
// taken to be in a method
var result = (/*submission here*/)(/*arguments*/);
(puts the result into a variable so it can used for anything, it doesn't matter what - note that the compiler knows the type, hence var
works here, and that the extra parentheses save having to include them in the submission)
Note: this is part of a broader concern about submitting code which simply doesn't compile. Generally, it seems wrong to have to put the code provided in any sort of 'environment' to get it running: when does this extra stuff stop being the 'environment' and start being code? We don't count the code required to call the compiler, but why not? C# is only meaningful if you compile it with the right compiler, etc.
Note: this is something that has been discussed around a lot, but I can't find a satisfactory consensus. It's also something that most people seem not to care about too much, which is fine by me, but when I'm voting, I like to vote for answer which are creative, show a serious attempt at golfing (I can't just pull 5 bytes out of them without thinking), and comply with the rules: if the rules aren't defined, then I struggle to up-vote stuff. I do generally up-vote (and down-vote) C#, Java, PoSh, and even F# answers more than others, because I know them all well enough that I could have a serious go at golfing them if I could be bothered.
code(args)
" but "function submissions must either leave a named function defined or must be expressions that evaluate to a function". In the latter case, whether that expression is directly callable or not due to precedence concerns is usually ignored in many languages. \$\endgroup\$