Lambda Expressions
Lambda expressions in Y are anonymous functions that can be created inline. They provide a concise way to define functions without explicit names, especially useful for short operations and higher-order function programming.
Lambda Syntax
The basic lambda syntax uses the \
character followed by parameters and a body:
\(param1, param2) => expression
Simple Lambdas
// Identity function
let identity = \(x) => x;
// Simple arithmetic
let add_one = \(x) => x + 1;
let multiply = \(x, y) => x * y;
// Boolean operations
let is_even = \(n) => n % 2 == 0;
Lambda Types
Lambdas have function types that can be explicitly specified:
let doubler: (i64) -> i64 = \(x) => x * 2;
let comparator: (i64, i64) -> bool = \(a, b) => a > b;
let processor: (str) -> void = \(s) => printf(s);
Using Lambdas
As Variables
let square = \(n) => n * n;
let result = square(5); // 25
let max = \(a, b) => if (a > b) { a } else { b };
let bigger = max(10, 20); // 20
As Function Arguments
fn apply_to_number(n: i64, func: (i64) -> i64): i64 {
func(n)
}
// Using lambda directly as argument
let result = apply_to_number(10, \(x) => x * 3); // 30
fn apply_operation(x: i64, y: i64, op: (i64, i64) -> i64): i64 {
op(x, y)
}
// Lambda for custom operations
let sum = apply_operation(5, 7, \(a, b) => a + b); // 12
let product = apply_operation(3, 4, \(a, b) => a * b); // 12
Lambdas in Structs
Lambdas can be stored in struct fields:
struct Processor {
transform: (i64) -> i64;
name: str;
}
let doubler_proc = Processor {
transform: \(x) => x * 2,
name: "Doubler"
};
let result = doubler_proc.transform(21); // 42
Examples from Y Code
Basic Lambda Usage
fn main(): i64 {
// Lambda with explicit type annotation
let x: (i64) -> i64 = \(x) => x;
// Using the lambda
let result = x(42);
return result;
}
Lambdas as Return Values
fn foobar(): (i64) -> i64 {
return \(x) => x; // Return lambda expression
}
fn create_multiplier(factor: i64): (i64) -> i64 {
return \(x) => x * factor;
}
// Usage
let times_three = create_multiplier(3);
let result = times_three(10); // 30
Lambdas with Complex Logic
// Multi-statement lambdas (using blocks)
let complex_processor = \(x) => {
let doubled = x * 2;
let incremented = doubled + 1;
incremented
};
// Conditional lambdas
let abs_value = \(x) => if (x < 0) { -x } else { x };
// Lambda with multiple parameters
let distance = \(x1, y1, x2, y2) => {
let dx = x2 - x1;
let dy = y2 - y1;
sqrt(dx * dx + dy * dy)
};
Higher-Order Programming
Function Composition
fn compose(f: (i64) -> i64, g: (i64) -> i64): (i64) -> i64 {
return \(x) => f(g(x));
}
let add_one = \(x) => x + 1;
let double = \(x) => x * 2;
let add_then_double = compose(double, add_one);
let result = add_then_double(5); // (5 + 1) * 2 = 12
Array Processing (Conceptual)
// If Y had higher-order array functions:
fn map(arr: &[i64], func: (i64) -> i64): &[i64] {
// Implementation would go here
return arr; // Placeholder
}
// Usage with lambdas
let numbers = &[1, 2, 3, 4, 5];
let doubled = map(numbers, \(x) => x * 2); // [2, 4, 6, 8, 10]
let squared = map(numbers, \(x) => x * x); // [1, 4, 9, 16, 25]
Event Handlers (Conceptual)
struct Button {
label: str;
on_click: () -> void;
}
let save_button = Button {
label: "Save",
on_click: \() => printf("Saving data...\n")
};
let cancel_button = Button {
label: "Cancel",
on_click: \() => printf("Operation cancelled\n")
};
Practical Examples
Mathematical Operations
struct MathOperations {
sin: (f64) -> f64;
cos: (f64) -> f64;
square: (f64) -> f64;
}
let math = MathOperations {
sin: \(x) => external_sin(x), // Assuming external function
cos: \(x) => external_cos(x),
square: \(x) => x * x
};
Data Transformation
fn transform_data(value: i64, transformer: (i64) -> i64): i64 {
transformer(value)
}
// Different transformations
let normalized = transform_data(150, \(x) => x / 100); // 1
let clamped = transform_data(150, \(x) => if (x > 100) { 100 } else { x }); // 100
let negated = transform_data(150, \(x) => -x); // -150
Configuration and Callbacks
struct Config {
validator: (str) -> bool;
formatter: (str) -> str;
processor: (str) -> void;
}
let email_config = Config {
validator: \(email) => contains(email, "@"), // Conceptual
formatter: \(email) => to_lowercase(email), // Conceptual
processor: \(email) => send_email(email) // Conceptual
};
Lambda Limitations
- Single Expression: Lambdas work best with single expressions (though blocks can be used)
- Type Inference: May need explicit type annotations in some contexts
- Closure Scope: Currently limited closure capabilities
Best Practices
When to Use Lambdas
// Good: Short, simple operations
let double = \(x) => x * 2;
let is_positive = \(x) => x > 0;
// Good: One-time use in function calls
process_array(data, \(x) => x + 1);
// Consider named function: Complex logic
fn complex_validation(input: str): bool {
// Multiple checks and logic
return true; // Placeholder
}
Readability
// Good: Clear and concise
let operations = &[
\(x) => x + 1,
\(x) => x * 2,
\(x) => x - 1
];
// Less readable: Too complex for lambda
let complex = \(x) => {
let temp = x * 2;
let adjusted = temp + 10;
if (adjusted > 100) { 100 } else { adjusted }
};
// Better as named function:
fn complex_transform(x: i64): i64 {
let temp = x * 2;
let adjusted = temp + 10;
if (adjusted > 100) { 100 } else { adjusted }
}
Type Clarity
// Good: Explicit types when needed
let processor: (str) -> bool = \(s) => validate(s);
// Good: Clear parameter names
let distance_calc = \(x1, y1, x2, y2) =>
sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
// Less clear: Unclear parameters
let calc = \(a, b, c, d) => sqrt((c - a) * (c - a) + (d - b) * (d - b));