I have a function that repeatedly calls another function.
The second function has a bool parameter that changes the way it behaves, so when I call the first function I want to have a parameter that specifies the way the second function behaves.
void Function1(int n, bool differentBehavior = false)
{
int a = Function2(n, differentBehavior);
int b = Function2(1, differentBehavior);
int c = Function2(2, differentBehavior);
return a b c;
}
int Function2(int x, bool differentBehavior)
{
if (!differentBehavior) // do something
else // do something else
}
The code itself is obviously an example (in reality the second function is called over 20 times and for code readability I would love to not have to specify the second parameter every time), but I put it there to explain what I'm currently doing. Is there no better way to achieve this?
CodePudding user response:
You can introduce a local function to capture the second argument like so:
int Function1(int n, bool differentBehavior = false)
{
int func(int n) => Function2(n, differentBehavior);
int a = func(n);
int b = func(1);
int c = func(2);
return a b c;
}
This is called "partial function application". See more here:
https://codeblog.jonskeet.uk/2012/01/30/currying-vs-partial-function-application/
CodePudding user response:
While C# doesn't support true function Currying nor first-class partial function application, you can always
use a new locally scoped function (aka a local function) to wrap your Function2
with predefined arguments... which is conceptually almost the same thing as partial application, just without referential-transparency, and awkward delegate types.
Anyway, if you want to pass the outer Function1
's differentBehavior
argument value to Function2
then you will need to use a closure, which will capture the variable, but this will introduce slight runtime performance complications: as a closure generally means a GC heap allocation and copying function local state from the stack onto the heap and yada yada.
However, if you're only using constant parameter values - or you're okay with using different wrappers for different predefined argument values, then you can use a static
local function (requires C# 8.0 or later) which prevents you from unintentionally creating a closure.
For example:
void Function1(int n, bool differentBehavior = false)
{
// Look ma, no closure!
static int PartiallyAppliedFunc2_False(int x) => Function2( x: x, differentBehavior: false );
static int PartiallyAppliedFunc2_True(int x) => Function2( x: x, differentBehavior: true );
int a = PartiallyAppliedFunc2_False(n);
int b = PartiallyAppliedFunc2_False(1);
int c = PartiallyAppliedFunc2_True(2);
return a b c;
}
int Function2(int x, bool differentBehavior)
{
if (!differentBehavior) // do something
else // do something else
}
CodePudding user response:
One thing to look at when a lot of parameters are being passed on the stack is whether there is some higher-level state that could be represented by a member variable of the class.
Here's some code for the most basic kind of state machine. This general approach might help solve the problem you're having:
class Program
{
enum AppState
{
Idle,
Initial,
Busy,
}
static AppState State { get; set; }
static void Main(string[] args)
{
foreach (AppState state in Enum.GetValues(typeof(AppState)))
{
Console.WriteLine($"Function returned { Function1(1)}");
}
int Function1(int n)
{
int a = Function2(n);
int b = Function2(1);
int c = Function2(2);
return a b c;
}
int Function2(int x)
{
switch (State)
{
case AppState.Idle:
return 10;
case AppState.Initial:
return 20;
case AppState.Busy:
return 30;
default:
throw new NotImplementedException();
}
}
}
}