Search code examples
c#if-statementreturndeferredfunc

c# deferred evaluation and returned values


Considering the examples below, I'm confused about why the result for the first example is 4, 8, 6 and the second is 4, 8, 3 ? To me in the first example, only succ(3) and dub(4) are evaluated, and y should be 3? Can someone please help to explain how it works? Many thanks!

Example one:

public class Hello {
   public static void Main(string[] args) {
     int y = 0;
     Func<int,bool> even = (n) => { return n%2 == 0; }; 
     Func<int,int> dub = (n) => { y += 2; return n + n; };
     Func<int,int> succ = (n) => { y += 1; return n + 1; };

     Func<bool, int, int, int> if1 = (c, t, f) => c? t: f;

     y = 0;
     var a1 = if1(even(3), dub(3), succ(3));
     var a2 = if1(even(4), dub(4), succ(4)); 
     Console.WriteLine("{0} {1} {2}", a1, a2, y);   
   }
 }

Second example:

public class Hello {
public static void Main(string[] args) {
  int y = 0;
  Func<int,bool> even = (n) => { return n%2 == 0; }; 
  Func<int,int> dub = (n) => { y += 2; return n + n; };
  Func<int,int> succ = (n) => { y += 1; return n + 1; };

  Func<Func<int,bool>, Func<int,int>, Func<int,int>, Func<int,int>> if2 = (c, t, f) => (n) => { if (c(n)) return t(n); else return f(n); };

   y = 0;
   Func<int,int> x = if2(even, dub, succ); 
   var c1 = x(3);
   var c2 = x(4);
   Console.WriteLine("{0} {1} {2}", c1, c2, y);
 }
}

Solution

  • You're confusing two different things:

    • The conditional operator
    • Method arguments

    All method arguments are evaluated before the method is called, even if they inside the method end up not being used.

    So the fact that you are using the conditional operator inside the method has no bearing on the arguments, they are all evaluated.

    So this call:

    var a1 = if1(even(3), dub(3), succ(3));
    

    Will evaluate all of the following:

    • even(3)
    • dub(3)
    • succ(3)

    before the method is actually called. If you instead of the direct result had returned a method that could be called to obtain the results you would have true deferred code (in this scenario) but I daresay the code would start to look really ugly.

                                       v---v
    Func<int, Func<int>> dub = (n) =>  () => { y += 2; return n + n; };
    Func<int, Func<int>> succ = (n) => () => { y += 1; return n + 1; };
    Func<bool, Func<int>, Func<int>, int> if1 = (c, t, f) => c ? t() : f();
               ^-------------------^                              ^^    ^^
    

    Additionally, as @atlaste says in the comment to your question, debugging would reveal how this works, you would see that before you step into if1 you would step through all of the other methods.