Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Type Annotations

Type annotations in Y provide explicit type information that helps with code clarity, type checking, and compiler optimization. While Y has type inference capabilities, explicit type annotations are required in certain contexts and recommended for complex types.

Basic Type Annotation Syntax

Type annotations use the colon (:) syntax:

let variable_name: Type = value;
fn function_name(param: Type): ReturnType { ... }

Variable Type Annotations

Simple Types

let age: i64 = 25;
let price: f64 = 99.99;
let name: str = "Alice";
let is_active: bool = true;
let grade: char = 'A';

When Type Inference Isn't Enough

// Empty arrays need explicit types
let numbers: &[i64] = &[];
let names: &[str] = &[];

// Ambiguous numeric literals
let count: u32 = 42;  // Specify u32 instead of default i64

Function Type Annotations

Function Parameters

All function parameters require type annotations:

fn calculate_area(width: f64, height: f64): f64 {
    width * height
}

fn process_user(user: User, active: bool): void {
    // Function implementation
}

Return Types

Functions must specify their return type:

fn get_name(): str {
    "Anonymous"
}

fn is_valid(input: str): bool {
    input.len() > 0
}

fn process_data(): void {
    // No return value
}

Complex Type Annotations

Function Types

Function types use the (param_types) -> return_type syntax:

// Function that takes two i64s and returns i64
let binary_op: (i64, i64) -> i64 = add;

// Function that takes no parameters and returns str
let getter: () -> str = get_default_name;

// Function that takes a function as parameter
let processor: ((i64) -> i64) -> i64 = apply_twice;

Array Types

Array types specify the element type:

let scores: &[i64] = &[95, 87, 92];
let names: &[str] = &["Alice", "Bob", "Charlie"];
let flags: &[bool] = &[true, false, true];

// Multidimensional arrays
let matrix: &[&[i64]] = &[&[1, 2], &[3, 4]];

Struct Types

Struct types use the struct name:

struct Point {
    x: f64;
    y: f64;
}

let origin: Point = Point { x: 0.0, y: 0.0 };
let location: Point = calculate_position();

Advanced Type Annotations

Higher-Order Function Types

Complex function types that involve functions as parameters or return values:

// Function that takes a function and returns a function
let transformer: ((i64) -> i64) -> ((i64) -> i64) = create_transformer;

// Function that takes multiple function parameters
let combiner: ((i64) -> i64, (i64) -> i64, i64) -> i64 = combine_operations;

// Function returning a function that takes a function
let factory: () -> ((str) -> bool) = create_validator;

Nested Function Types

// Function that processes arrays with custom logic
let array_processor: (&[i64], (i64) -> bool) -> &[i64] = filter_array;

// Event handler type
let event_handler: (str, () -> void) -> void = register_handler;

// Complex data transformer
let data_transform: (&[str], (str) -> str, (str) -> bool) -> &[str] = process_strings;

Examples from Y Code

Function Variables with Type Annotations

fn add(x: i64, y: i64): i64 {
    x + y
}

fn main(): i64 {
    // Explicit function type annotation
    let x: (i64) -> i64 = \(x) => x;

    // Using the annotated function
    let result = x(42);
    return result;
}

Struct with Function Fields

struct TestStruct {
    x: i64;
    bar: (i64, i64) -> i64;  // Function type in struct
}

let my_struct: TestStruct = TestStruct {
    x: 42,
    bar: add  // Function reference
};

Array with Type Annotation

fn main(): void {
    let x: &[i64] = &[];  // Empty array needs type annotation
    x[0];  // Access array element
}

Type Annotations in Practice

Configuration Objects

struct DatabaseConfig {
    host: str;
    port: i64;
    timeout: f64;
    ssl_enabled: bool;
}

struct ServerConfig {
    database: DatabaseConfig;
    max_connections: i64;
    request_handler: (str) -> str;  // Function type
}

let config: ServerConfig = ServerConfig {
    database: DatabaseConfig {
        host: "localhost",
        port: 5432,
        timeout: 30.0,
        ssl_enabled: true
    },
    max_connections: 100,
    request_handler: process_request
};

Generic-like Patterns

// Processing functions with explicit types
let string_processor: (str) -> str = normalize_string;
let number_processor: (i64) -> i64 = validate_number;
let array_processor: (&[i64]) -> &[i64] = sort_array;

// Data validation
let email_validator: (str) -> bool = is_valid_email;
let age_validator: (i64) -> bool = is_valid_age;
let password_validator: (str) -> bool = is_strong_password;

Callback Systems

struct EventSystem {
    on_click: () -> void;
    on_hover: (i64, i64) -> void;  // x, y coordinates
    on_key: (char) -> bool;        // key pressed, return if handled
}

let ui_events: EventSystem = EventSystem {
    on_click: handle_click,
    on_hover: \(x, y) => update_cursor(x, y),
    on_key: \(key) => process_key_input(key)
};

When Type Annotations Are Required

Function Signatures

// Required: All function parameters and return types
fn process(input: str, count: i64): bool {
    // Implementation
    return true;
}

Empty Collections

// Required: Empty arrays need type specification
let empty_numbers: &[i64] = &[];
let empty_strings: &[str] = &[];

Ambiguous Contexts

// Required when type can't be inferred
let processor: (str) -> bool = get_validator();
let data: &[i64] = get_array_data();

When Type Annotations Are Optional

Simple Variable Assignments

// Optional: Type can be inferred
let name = "Alice";     // Inferred as str
let age = 25;           // Inferred as i64
let price = 99.99;      // Inferred as f64
let active = true;      // Inferred as bool

Return Statements

// Type inferred from function signature
fn get_count(): i64 {
    return 42;  // Type inferred as i64
}

Best Practices

Clarity Over Brevity

// Good: Clear intent with explicit types
let user_validator: (User) -> bool = validate_user_data;
let error_handler: (str) -> void = log_error;

// Acceptable: When type is obvious
let name = "Alice";
let count = 42;

Complex Function Types

// Good: Break down complex types
type ValidationFunction = (str) -> bool;
type ProcessingFunction = (str, ValidationFunction) -> str;

let process_with_validation: ProcessingFunction = complex_processor;

// Less readable: Inline complex types
let processor: (str, (str) -> bool) -> str = complex_processor;

Consistent Style

// Good: Consistent annotation style
let config: Config = load_config();
let validator: (str) -> bool = create_validator();
let processor: (Config) -> Result = process_config;

// Inconsistent: Mixed annotation styles
let config = load_config();  // No annotation
let validator: (str) -> bool = create_validator();  // With annotation
let processor = process_config;  // No annotation

Documentation Through Types

// Good: Types that document intent
let error_logger: (str, i64) -> void = log_error_with_code;
let data_transformer: (&[str]) -> &[str] = clean_and_normalize;
let connection_manager: (str, i64) -> bool = establish_connection;

// Less clear: Generic types
let func1: (str, i64) -> void = log_error_with_code;
let func2: (&[str]) -> &[str] = clean_and_normalize;