Functions¶
Functions are reusable blocks of code that can be called multiple times. They accept parameters, execute statements, and optionally return values.
Function Declaration¶
Basic Syntax¶
Example:
No Parameters¶
Multiple Parameters¶
function calculatePremium(age, coverage, risk) {
basePremium = coverage * 0.001;
if (age < 25) {
basePremium *= 1.5;
} else if (age > 65) {
basePremium *= 1.25;
}
if (risk == "high") {
basePremium *= 1.3;
}
return $Round(basePremium, 2);
}
premium = calculatePremium(30, 250000, "low");
Return Statement¶
Returning Values¶
Early Return¶
function divide(a, b) {
if (b == 0) {
return null; // Early exit
}
return a / b;
}
result = divide(10, 2); // 5
error = divide(10, 0); // null
No Return Value¶
Functions without an explicit return return null:
function logMessage(message) {
$Log(message);
// No return statement
}
result = logMessage("Hello"); // result is null
Multiple Returns¶
function categorize(score) {
if (score >= 90) {
return "A";
} else if (score >= 80) {
return "B";
} else if (score >= 70) {
return "C";
} else if (score >= 60) {
return "D";
} else {
return "F";
}
}
Parameters¶
Parameter Count¶
Functions must be called with the exact number of parameters:
function calculate(a, b, c) {
return a + b + c;
}
result = calculate(1, 2, 3); // OK: 6
// calculate(1, 2); // ERROR: Too few arguments
// calculate(1, 2, 3, 4); // ERROR: Too many arguments
Parameter Naming¶
Parameters are local variables within the function:
function multiply(x, y) {
result = x * y; // x, y are local parameters
return result; // result is local variable
}
output = multiply(4, 5); // 20
// x is not accessible here
No Default Parameters¶
EverSharp does not support default parameter values. Handle missing cases explicitly:
function greet(name) {
if (name == null || name == "") {
name = "Guest"; // Manual default
}
return "Hello, " + name;
}
No Named Parameters¶
Parameters must be passed positionally:
function createPolicy(number, premium, coverage) {
return {
number: number,
premium: premium,
coverage: coverage,
};
}
// Must pass in order
policy = createPolicy("POL-001", 1200, 250000);
// Cannot use named parameters (not supported)
// policy = createPolicy(number: "POL-001", premium: 1200, coverage: 250000);
Function Scope¶
Local Variables¶
Variables declared inside functions are local:
function calculate() {
local = 10; // Local to calculate()
return local * 2;
}
result = calculate(); // 20
// local is not accessible here
Parameters are Local¶
function process(value) {
value = value * 2; // Modifies local copy
return value;
}
original = 10;
result = process(original); // 20
// original is still 10 (not modified)
Accessing Global Variables¶
Functions can read and write global variables:
taxRate = 0.08; // Global
function calculateTotal(subtotal) {
return subtotal * (1 + taxRate);
}
total = calculateTotal(100); // 108
Shadowing¶
Local variables shadow globals with the same name:
value = 100; // Global
function process() {
value = 50; // Creates local variable
return value;
}
result = process(); // 50
// value is still 100 (global not modified)
To modify global:
value = 100; // Global
function updateGlobal() {
value = 50; // Modifies global
}
updateGlobal();
// value is now 50
Recursion¶
Functions can call themselves:
Factorial¶
function factorial(n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
result = factorial(5); // 120
Fibonacci¶
function fibonacci(n) {
if (n <= 1) {
return n;
}
return fibonacci(n - 1) + fibonacci(n - 2);
}
fib10 = fibonacci(10); // 55
Recursive Array Sum¶
function sumArray(arr, index) {
if (index >= arr.Length) {
return 0;
}
return arr[index] + sumArray(arr, index + 1);
}
numbers = [1, 2, 3, 4, 5];
total = sumArray(numbers, 0); // 15
Note: Be careful with recursion depth. Large recursions may cause stack overflow.
First-Class Functions¶
Functions are values and can be:
Assigned to Variables¶
Passed as Arguments¶
function apply(func, value) {
return func(value);
}
function double(x) {
return x * 2;
}
function square(x) {
return x * x;
}
result1 = apply(double, 5); // 10
result2 = apply(square, 5); // 25
Returned from Functions¶
function multiplier(factor) {
function multiply(value) {
return value * factor;
}
return multiply;
}
double = multiplier(2);
triple = multiplier(3);
result1 = double(5); // 10
result2 = triple(5); // 15
Closures¶
Functions capture variables from their enclosing scope:
Basic Closure¶
function makeCounter() {
count = 0;
function increment() {
count++;
return count;
}
return increment;
}
counter1 = makeCounter();
counter1(); // 1
counter1(); // 2
counter1(); // 3
counter2 = makeCounter();
counter2(); // 1 (separate count)
Closure with Parameters¶
function makeAdder(x) {
function add(y) {
return x + y; // Captures x
}
return add;
}
add5 = makeAdder(5);
add10 = makeAdder(10);
result1 = add5(3); // 8
result2 = add10(3); // 13
Multiple Closures¶
function createCalculator(initial) {
value = initial;
function add(x) {
value += x;
return value;
}
function subtract(x) {
value -= x;
return value;
}
function getValue() {
return value;
}
return {
add: add,
subtract: subtract,
getValue: getValue,
};
}
calc = createCalculator(100);
calc.add(50); // 150
calc.subtract(30); // 120
current = calc.getValue(); // 120
Common Patterns¶
Pattern 1: Validation Function¶
function validateAge(age) {
if (age == null) {
return false;
}
if (age < 18 || age > 100) {
return false;
}
return true;
}
if (validateAge(customerAge)) {
// Process
}
Pattern 2: Calculation Function¶
function calculateDiscount(premium, customerType, years) {
discount = 0;
if (customerType == "premium") {
discount = 0.15;
} else if (customerType == "standard") {
discount = 0.1;
}
if (years > 5) {
discount += 0.05;
}
return premium * discount;
}
Pattern 3: Factory Function¶
function createPolicy(number, premium) {
return {
number: number,
premium: premium,
isActive: true,
createdDate: $Today(),
};
}
policy1 = createPolicy("POL-001", 1200);
policy2 = createPolicy("POL-002", 1500);
Pattern 4: Guard Clauses¶
function processPayment(payment) {
if (payment == null) {
return { success: false, error: "Payment is null" };
}
if (payment.amount <= 0) {
return { success: false, error: "Invalid amount" };
}
if (payment.method == null) {
return { success: false, error: "Payment method required" };
}
// Process payment
return { success: true, transactionId: generateId() };
}
Pattern 5: Helper Functions¶
function isValidEmail(email) {
if (email == null) return false;
return email.Contains("@") && email.Contains(".");
}
function isValidPhone(phone) {
if (phone == null) return false;
return phone.Length >= 10;
}
function validateCustomer(customer) {
if (!isValidEmail(customer.email)) {
return false;
}
if (!isValidPhone(customer.phone)) {
return false;
}
return true;
}
Pattern 6: Mapper Function¶
function mapPolicyToDTO(policy) {
return {
policyNumber: policy.number,
customerName: policy.customer.firstName + " " + policy.customer.lastName,
annualPremium: policy.premium * 12,
status: policy.isActive ? "Active" : "Inactive",
};
}
policies = getPolicies();
dtos = policies.Map((p) => mapPolicyToDTO(p));
Function Composition¶
Combine functions to create complex behavior:
function add10(x) {
return x + 10;
}
function double(x) {
return x * 2;
}
function square(x) {
return x * x;
}
// Compose functions
result = square(double(add10(5))); // ((5 + 10) * 2)^2 = 900
// Or step by step
step1 = add10(5); // 15
step2 = double(step1); // 30
step3 = square(step2); // 900
Best Practices¶
1. Single Responsibility¶
Each function should do one thing:
// Less ideal: Does too much
function processPolicy(policy) {
// Validates
// Calculates premium
// Saves to database
// Sends email
}
// Better: Separate concerns
function validatePolicy(policy) {}
function calculatePremium(policy) {}
function savePolicy(policy) {}
function sendNotification(policy) {}
2. Keep Functions Small¶
Target ~20 lines or fewer:
// Less ideal: Large function
function processOrder(order) {
// 50+ lines of logic
}
// Better: Break into smaller functions
function validateOrder(order) {}
function calculateTotal(order) {}
function applyDiscounts(order) {}
function finalizeOrder(order) {}
3. Use Descriptive Names¶
4. Minimize Side Effects¶
// Has side effect (modifies global)
function addToTotal(value) {
globalTotal += value; // Modifies global
}
// Pure function (no side effects)
function calculateNewTotal(currentTotal, value) {
return currentTotal + value;
}
5. Return Early¶
// Less readable
function validate(data) {
valid = false;
if (data != null) {
if (data.value > 0) {
if (data.name != "") {
valid = true;
}
}
}
return valid;
}
// More readable
function validate(data) {
if (data == null) return false;
if (data.value <= 0) return false;
if (data.name == "") return false;
return true;
}
6. Document Complex Logic¶
function calculateAdjustedPremium(age, coverage, history) {
// Base calculation uses industry standard rate of 0.001 per dollar
basePremium = coverage * 0.001;
// Age adjustment: 50% increase for under 25, 25% for over 65
if (age < 25) {
basePremium *= 1.5;
} else if (age > 65) {
basePremium *= 1.25;
}
// History adjustment: -20% for excellent, +30% for poor
if (history == "excellent") {
basePremium *= 0.8;
} else if (history == "poor") {
basePremium *= 1.3;
}
return $Round(basePremium, 2);
}
Common Mistakes¶
Mistake 1: Wrong Argument Count¶
function add(a, b) {
return a + b;
}
// result = add(5); // ERROR: Too few arguments
// result = add(5, 3, 1); // ERROR: Too many arguments
result = add(5, 3); // OK
Mistake 2: Expecting Default Parameters¶
// ERROR: Default parameters not supported
// function greet(name = "Guest") {
// return "Hello, " + name;
// }
// CORRECT: Handle manually
function greet(name) {
if (name == null) {
name = "Guest";
}
return "Hello, " + name;
}
Mistake 3: Modifying Parameters Without Intent¶
function process(value) {
value = value * 2; // Modifies local copy only
return value;
}
original = 10;
result = process(original); // 20
// original is still 10
Mistake 4: Infinite Recursion¶
// ERROR: Missing base case
// function loop(n) {
// return loop(n); // Infinite recursion!
// }
// CORRECT: Include base case
function countdown(n) {
if (n <= 0) {
return 0; // Base case
}
return countdown(n - 1);
}
Mistake 5: Forgetting Return¶
// ERROR: Forgot return
function add(a, b) {
result = a + b; // Calculated but not returned
// Missing: return result;
}
output = add(5, 3); // null (no return value)
// CORRECT
function add(a, b) {
result = a + b;
return result;
}
Advanced Examples¶
Example 1: Premium Calculator¶
function calculateInsurancePremium(policy) {
basePremium = calculateBasePremium(policy.coverage);
ageAdjustment = getAgeAdjustment(policy.customer.age);
historyDiscount = getHistoryDiscount(policy.customer.yearsWithCompany);
riskMultiplier = getRiskMultiplier(policy.riskCategory);
adjustedPremium = basePremium * ageAdjustment * riskMultiplier;
finalPremium = adjustedPremium - historyDiscount;
return $Round(finalPremium, 2);
}
function calculateBasePremium(coverage) {
return coverage * 0.001;
}
function getAgeAdjustment(age) {
if (age < 25) return 1.5;
if (age > 65) return 1.25;
return 1.0;
}
function getHistoryDiscount(years) {
if (years > 10) return 200;
if (years > 5) return 100;
return 0;
}
function getRiskMultiplier(category) {
if (category == "high") return 1.3;
if (category == "low") return 0.9;
return 1.0;
}
Example 2: Data Validator¶
function validatePolicyData(policy) {
errors = [];
validateRequired(policy, "number", errors);
validateRequired(policy, "customer", errors);
validateRequired(policy, "coverage", errors);
if (policy.coverage != null) {
validatePositive(policy.coverage, "Coverage", errors);
}
if (policy.customer != null) {
validateCustomer(policy.customer, errors);
}
return errors;
}
function validateRequired(obj, field, errors) {
value = $GetValue(obj, field);
if (value == null) {
errors.Push(field + " is required");
}
}
function validatePositive(value, name, errors) {
if (value <= 0) {
errors.Push(name + " must be positive");
}
}
function validateCustomer(customer, errors) {
if (customer.age < 18) {
errors.Push("Customer must be 18 or older");
}
if (customer.email == null || !customer.email.Contains("@")) {
errors.Push("Valid email required");
}
}
Example 3: Object Transformation¶
function transformPolicyForExport(policy) {
return {
policyNumber: policy.number,
customer: transformCustomer(policy.customer),
coverage: formatCurrency(policy.coverage),
premium: formatCurrency(policy.premium),
status: getStatusText(policy.isActive),
dates: {
created: formatDate(policy.createdDate),
expires: formatDate(policy.expirationDate),
},
};
}
function transformCustomer(customer) {
return {
fullName: customer.firstName + " " + customer.lastName,
contact: customer.email,
age: customer.age,
};
}
function formatCurrency(amount) {
return "$" + $ToString($Round(amount, 2));
}
function getStatusText(isActive) {
return isActive ? "Active" : "Inactive";
}
function formatDate(date) {
return (
$ToString($Year(date)) + "-" + pad($Month(date)) + "-" + pad($Day(date))
);
}
function pad(number) {
return number < 10 ? "0" + $ToString(number) : $ToString(number);
}
Next Steps¶
- Explore lambda expressions: Lambda Expressions
- Learn about closures in depth: Lambda Expressions
- See practical examples: Examples
- Understand objects: Objects