uCalc API Version: 2.1.3-preview.2 Released: 6/17/2026

Warning

uCalc API Preview Release Notice:The documentation describes the intended behavior of the API. The current preview build contains incomplete features, unoptimized performance, and is subject to breaking changes.

Lazy Evaluation (ByExpr)

Product: 

Class: 

Explains how uCalc's ByExpr modifier enables lazy evaluation, allowing for the creation of custom control structures and short-circuiting logic.

Remarks

💤 Lazy Evaluation with ByExpr

Most programming languages and expression evaluators use eager evaluation. This means that when you call a function, all of its arguments are fully calculated before the function itself begins to run. While simple, this approach makes it impossible to create custom control structures like conditional statements or loops.

ByExpr is the powerful uCalc modifier that solves this problem by enabling lazy evaluation.


1. The Problem with Eager Evaluation

Imagine trying to create your own if function:MyIf(condition, then_branch, else_branch)

With eager evaluation, if you call MyIf(1 > 0, 100, 1/0), the program will crash. Even though the else_branch containing 1/0 should never be executed, the evaluator calculates it before the MyIf function even has a chance to check the condition. This fundamental limitation prevents standard evaluators from implementing custom control flow.


2. The uCalc Solution: ByExpr

The ByExpr modifier changes how an argument is passed to a callback function. Instead of passing the argument's final value, it passes an unevaluated Expression object.

Your callback function then has complete control over this Expression object. It can:

  • Evaluate it: Call .Evaluate() or .Execute() to get its result.
  • Ignore it: Choose not to evaluate it at all (the key to short-circuiting).
  • Evaluate it multiple times: The foundation for creating custom loops.

Inside the callback, you retrieve this object using the cb.ArgExpr(n) method.


💡 Why uCalc? (Comparative Analysis)

  • vs. C# Delegates/Lambdas (Func<T>): Passing an argument ByExpr is conceptually similar to passing a Func<T> delegate in C#. Both techniques provide a piece of code to be executed later. However, uCalc's approach is designed for a dynamic, string-based scripting environment. A key advantage is that uCalc's Expression object allows for metaprogramming; [you can inspect the argument's original text before deciding whether to execute it, a capability not available with compiled lambdas].

  • vs. Functional Languages (LISP, Haskell): Lazy evaluation is a core concept in many functional programming languages. ByExpr brings this powerful paradigm to mainstream imperative languages like C#, C++, and VB, allowing developers to build more expressive and powerful control structures without leaving their familiar environment.

  • vs. Standard eval() Functions: Built-in eval() functions in scripting languages are almost always eager, making them incapable of creating custom control structures. uCalc's ByExpr elevates it from a simple calculator to a lightweight language construction kit.

Examples

Succinct: Creates a custom `IIf` function to demonstrate basic lazy evaluation. The division-by-zero error in the unevaluated branch is safely ignored.
				
					using uCalcSoftware;

var uc = new uCalc();

static void MyIIf(uCalc.Callback cb) {
   var condition = cb.ArgBool(1);
   var thenPart = cb.ArgExpr(2);
   var elsePart = cb.ArgExpr(3);

   if (condition) {
      cb.Return(thenPart.Evaluate());
   } else {
      cb.Return(elsePart.Evaluate());
   }
}

uc.DefineFunction("MyIIf(cond As Bool, ByExpr thenExpr, ByExpr elseExpr)", MyIIf);

// The 'else' branch contains 1/0, but since the condition is true,
// it is never evaluated.
Console.WriteLine(uc.Eval("MyIIf(10 > 5, 100, 1/0)"));

// The 'then' branch contains 1/0, but it is never evaluated.
Console.WriteLine(uc.Eval("MyIIf(10 < 5, 1/0, 200)"));
				
			
100
200
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

void ucalc_call MyIIf(uCalcBase::Callback cb) {
   auto condition = cb.ArgBool(1);
   auto thenPart = cb.ArgExpr(2);
   auto elsePart = cb.ArgExpr(3);

   if (condition) {
      cb.Return(thenPart.Evaluate());
   } else {
      cb.Return(elsePart.Evaluate());
   }
}
int main() {
   uCalc uc;
   uc.DefineFunction("MyIIf(cond As Bool, ByExpr thenExpr, ByExpr elseExpr)", MyIIf);

   // The 'else' branch contains 1/0, but since the condition is true,
   // it is never evaluated.
   cout << uc.Eval("MyIIf(10 > 5, 100, 1/0)") << endl;

   // The 'then' branch contains 1/0, but it is never evaluated.
   cout << uc.Eval("MyIIf(10 < 5, 1/0, 200)") << endl;
}
				
			
100
200
				
					Imports System
Imports uCalcSoftware
Public Module Program
   
   Public Sub MyIIf(ByVal cb As uCalc.Callback)
      Dim condition = cb.ArgBool(1)
      Dim thenPart = cb.ArgExpr(2)
      Dim elsePart = cb.ArgExpr(3)
      
      If condition Then
         cb.Return(thenPart.Evaluate())
      Else
         cb.Return(elsePart.Evaluate())
      End If
   End Sub
   Public Sub Main()
      Dim uc As New uCalc()
      uc.DefineFunction("MyIIf(cond As Bool, ByExpr thenExpr, ByExpr elseExpr)", AddressOf MyIIf)
      
      '// The 'else' branch contains 1/0, but since the condition is true,
      '// it is never evaluated.
      Console.WriteLine(uc.Eval("MyIIf(10 > 5, 100, 1/0)"))
      
      '// The 'then' branch contains 1/0, but it is never evaluated.
      Console.WriteLine(uc.Eval("MyIIf(10 < 5, 1/0, 200)"))
   End Sub
End Module
				
			
100
200
Practical: Builds a custom `Repeat` loop control structure that executes an action a specified number of times.
				
					using uCalcSoftware;

var uc = new uCalc();

static void MyRepeat(uCalc.Callback cb) {
   var count = cb.ArgInt32(1);
   var action = cb.ArgExpr(2);
   var i = 0;
   for ( i = 1; i <= count; i++) {
      action.Execute(); // Evaluate the expression on each iteration
   }
}

// Define a counter variable in the engine
uc.DefineVariable("counter = 0");

// The action 'counter++' is passed as an unevaluated expression
uc.DefineFunction("Repeat(count As Int, ByExpr action)", MyRepeat);

// Execute the custom loop
uc.Eval("Repeat(5, counter++)");

Console.Write("Final counter value: ");
Console.WriteLine(uc.Eval("counter"));
				
			
Final counter value: 5
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

void ucalc_call MyRepeat(uCalcBase::Callback cb) {
   auto count = cb.ArgInt32(1);
   auto action = cb.ArgExpr(2);
   auto i = 0;
   for ( i = 1; i <= count; i++) {
      action.Execute(); // Evaluate the expression on each iteration
   }
}
int main() {
   uCalc uc;
   // Define a counter variable in the engine
   uc.DefineVariable("counter = 0");

   // The action 'counter++' is passed as an unevaluated expression
   uc.DefineFunction("Repeat(count As Int, ByExpr action)", MyRepeat);

   // Execute the custom loop
   uc.Eval("Repeat(5, counter++)");

   cout << "Final counter value: ";
   cout << uc.Eval("counter") << endl;
}
				
			
Final counter value: 5
				
					Imports System
Imports uCalcSoftware
Public Module Program
   
   Public Sub MyRepeat(ByVal cb As uCalc.Callback)
      Dim count = cb.ArgInt32(1)
      Dim action = cb.ArgExpr(2)
      Dim i = 0
      For i  = 1 To count
         action.Execute() '// Evaluate the expression on each iteration
      Next
   End Sub
   Public Sub Main()
      Dim uc As New uCalc()
      '// Define a counter variable in the engine
      uc.DefineVariable("counter = 0")
      
      '// The action 'counter++' is passed as an unevaluated expression
      uc.DefineFunction("Repeat(count As Int, ByExpr action)", AddressOf MyRepeat)
      
      '// Execute the custom loop
      uc.Eval("Repeat(5, counter++)")
      
      Console.Write("Final counter value: ")
      Console.WriteLine(uc.Eval("counter"))
   End Sub
End Module
				
			
Final counter value: 5
Internal Test: Implements a `ShortCircuitOr` function to prove that the second argument is not evaluated if the first is true, using side effects (counters) for verification.
				
					using uCalcSoftware;

var uc = new uCalc();

static void ShortCircuitOr(uCalc.Callback cb) {
   var arg1 = cb.ArgExpr(1);
   var arg2 = cb.ArgExpr(2);

   // Evaluate the first argument
   var result1 = arg1.EvaluateBool();

   // If the first is true, return immediately without touching the second
   if (result1) {
      cb.ReturnBool(true);
   } else {
      // Otherwise, evaluate and return the second argument's result
      cb.ReturnBool(arg2.EvaluateBool());
   }
}

static void FuncA(uCalc.Callback cb) {
   cb.uCalc.Eval("countA = countA + 1");
   cb.ReturnBool(true);
}

static void FuncB(uCalc.Callback cb) {
   cb.uCalc.Eval("countB = countB + 1");
   cb.ReturnBool(true);
}


uc.DefineVariable("countA = 0");
uc.DefineVariable("countB = 0");

uc.DefineFunction("FuncA() As Bool", FuncA);
uc.DefineFunction("FuncB() As Bool", FuncB);

uc.DefineFunction("SC_Or(ByExpr a, ByExpr b) As Bool", ShortCircuitOr);

Console.WriteLine("Calling SC_Or(FuncA(), FuncB())...");
uc.Eval("SC_Or(FuncA(), FuncB())");

Console.WriteLine($"FuncA was called {uc.Eval("countA")} time(s).");
Console.WriteLine($"FuncB was called {uc.Eval("countB")} time(s)."); // Should be 0
				
			
Calling SC_Or(FuncA(), FuncB())...
FuncA was called 1 time(s).
FuncB was called 0 time(s).
				
					#include <iostream>
#include "uCalc.h"

using namespace std;
using namespace uCalcSoftware;

void ucalc_call ShortCircuitOr(uCalcBase::Callback cb) {
   auto arg1 = cb.ArgExpr(1);
   auto arg2 = cb.ArgExpr(2);

   // Evaluate the first argument
   auto result1 = arg1.EvaluateBool();

   // If the first is true, return immediately without touching the second
   if (result1) {
      cb.ReturnBool(true);
   } else {
      // Otherwise, evaluate and return the second argument's result
      cb.ReturnBool(arg2.EvaluateBool());
   }
}

void ucalc_call FuncA(uCalcBase::Callback cb) {
   cb.uCalc().Eval("countA = countA + 1");
   cb.ReturnBool(true);
}

void ucalc_call FuncB(uCalcBase::Callback cb) {
   cb.uCalc().Eval("countB = countB + 1");
   cb.ReturnBool(true);
}

int main() {
   uCalc uc;
   uc.DefineVariable("countA = 0");
   uc.DefineVariable("countB = 0");

   uc.DefineFunction("FuncA() As Bool", FuncA);
   uc.DefineFunction("FuncB() As Bool", FuncB);

   uc.DefineFunction("SC_Or(ByExpr a, ByExpr b) As Bool", ShortCircuitOr);

   cout << "Calling SC_Or(FuncA(), FuncB())..." << endl;
   uc.Eval("SC_Or(FuncA(), FuncB())");

   cout << "FuncA was called " << uc.Eval("countA") << " time(s)." << endl;
   cout << "FuncB was called " << uc.Eval("countB") << " time(s)." << endl; // Should be 0
}
				
			
Calling SC_Or(FuncA(), FuncB())...
FuncA was called 1 time(s).
FuncB was called 0 time(s).
				
					Imports System
Imports uCalcSoftware
Public Module Program
   
   Public Sub ShortCircuitOr(ByVal cb As uCalc.Callback)
      Dim arg1 = cb.ArgExpr(1)
      Dim arg2 = cb.ArgExpr(2)
      
      '// Evaluate the first argument
      Dim result1 = arg1.EvaluateBool()
      
      '// If the first is true, return immediately without touching the second
      If result1 Then
         cb.ReturnBool(true)
      Else
         '// Otherwise, evaluate and return the second argument's result
         cb.ReturnBool(arg2.EvaluateBool())
      End If
   End Sub
   
   Public Sub FuncA(ByVal cb As uCalc.Callback)
      cb.uCalc.Eval("countA = countA + 1")
      cb.ReturnBool(true)
   End Sub
   
   Public Sub FuncB(ByVal cb As uCalc.Callback)
      cb.uCalc.Eval("countB = countB + 1")
      cb.ReturnBool(true)
   End Sub
   
   Public Sub Main()
      Dim uc As New uCalc()
      uc.DefineVariable("countA = 0")
      uc.DefineVariable("countB = 0")
      
      uc.DefineFunction("FuncA() As Bool", AddressOf FuncA)
      uc.DefineFunction("FuncB() As Bool", AddressOf FuncB)
      
      uc.DefineFunction("SC_Or(ByExpr a, ByExpr b) As Bool", AddressOf ShortCircuitOr)
      
      Console.WriteLine("Calling SC_Or(FuncA(), FuncB())...")
      uc.Eval("SC_Or(FuncA(), FuncB())")
      
      Console.WriteLine($"FuncA was called {uc.Eval("countA")} time(s).")
      Console.WriteLine($"FuncB was called {uc.Eval("countB")} time(s).") '// Should be 0
   End Sub
End Module
				
			
Calling SC_Or(FuncA(), FuncB())...
FuncA was called 1 time(s).
FuncB was called 0 time(s).