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

Data Types

Crush has a simple, dynamic type system with optional type hints. This chapter covers all built-in types and their usage.

Primitive Types

Int

Integer numbers (64-bit signed):

let count = 42;
let negative = -100;
let hex = 0xFF;
let binary = 0b1010;

// Type hint
let age: Int = 30;

Range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

Float

Floating-point numbers (64-bit IEEE 754):

let pi = 3.14159;
let scientific = 1.5e-10;
let negative = -2.5;

// Type hint
let temperature: Float = 98.6;

String

UTF-8 encoded text:

let name = "Alice";
let greeting = 'Hello';
let multiline = """
    This is a
    multi-line string
""";

// Type hint
let message: String = "Hello, World!";

String Operations:

// Concatenation
let full_name = first + " " + last;

// Length (via capability)
let len = str.len(name);

// Substring (via capability)
let sub = str.substring(text, 0, 5);

Bool

Boolean values:

let is_active = true;
let is_complete = false;

// Type hint
let flag: Bool = true;

// From comparisons
let result = x > 10;  // Bool

Bytes

Raw byte buffers for binary data:

let data = b"hello";

// Type hint
let buf: Bytes = b"data";

Error

First-class error values for exception handling:

let err = Error("file not found");

// Check error
if type.of(result) == "Error" {
    io.print("Failed: " + result.message);
}

Null

Represents absence of a value:

let empty = null;

// Type hint
let optional: String? = null;  // Future feature

// Checking for null
if value == null {
    io.print("No value");
}

Collection Types

Array

Ordered collection of values:

// Array literal
let numbers = [1, 2, 3, 4, 5];
let mixed = [1, "two", 3.0, true];  // Mixed types allowed

// Type hint (future feature)
// let scores: Array<Int> = [90, 85, 95];

// Empty array
let empty = [];

Array Operations:

// Access by index
let first = numbers[0];
let last = numbers[4];

// Length
let len = array.length(numbers);

// Append
array.push(numbers, 6);

// Remove last
let popped = array.pop(numbers);

// Iterate
for item in numbers {
    io.print(item);
}

Map (Object)

Key-value pairs:

// Map literal
let person = {
    "name": "Alice",
    "age": 30,
    "active": true
};

// Type hint (future feature)
// let config: Map<String, Int> = {"max": 100};

// Empty map
let empty = {};

Map Operations:

// Access by key
let name = person["name"];
let age = person.age;  // Dot notation

// Set value
person["email"] = "alice@example.com";
person.phone = "555-1234";

// Check key exists
if map.has_key(person, "email") {
    io.print("Email exists");
}

// Iterate
for key in map.keys(person) {
    let value = person[key];
    io.print(key + ": " + value);
}

Type Conversion

Explicit Conversion

// Int to String
let str = conv.to_string(42);

// String to Int
let num = conv.to_int("42");

// Float to Int
let rounded = conv.to_int(3.14);

// Int to Float
let decimal = conv.to_float(42);

Implicit Conversion

String concatenation auto-converts:

let message = "Count: " + 42;  // "Count: 42"
let result = "Pi is " + 3.14;  // "Pi is 3.14"

Type Checking

Runtime Type Checking

let value = 42;

// Check type
let type_name = type.of(value);  // "Int"

if type_name == "Int" {
    io.print("It's an integer");
}

Type Hints

Type hints are optional annotations:

// Variable type hints
let name: String = "Alice";
let age: Int = 30;
let score: Float = 95.5;
let active: Bool = true;

// Function parameter types
fn greet(name: String, age: Int) {
    io.print("Hello, " + name);
}

// Function return type
fn calculate(x: Int, y: Int) -> Int {
    return x + y;
}

Note: Type hints are currently for documentation only. Runtime type checking is dynamic.

Structs

Custom data structures are implemented (struct keyword, StructDef AST node, NewStruct expression):

struct Point {
    x: Float,
    y: Float
}

fn main() {
    let p = Point { x: 10.0, y: 20.0 };
    io.print(p.x);
}

Function Type

Functions are first-class values. The Function type in hints represents any callable:

fn apply(f: Function, x: Int) -> Int {
    return f(x);
}

// Lambda type hint
let cb: Function = |x| { return x * 2; };

Optional / Nullable Types

The Type? nullable hint syntax is recognized by the parser:

let name: String? = null;

if name != null {
    io.print(name);
}

Note: The ?? null-coalescing operator is not yet implemented; use an explicit if check instead.

Enums (Future Feature)

Enumerated types are not yet in the AST:

// Not yet supported:
// enum Status { Pending, Active, Complete }

Type Aliases (Future Feature)

// Not yet supported:
// type UserId = Int;

Type Inference

Crush infers types from values:

let x = 42;           // Inferred as Int
let pi = 3.14;        // Inferred as Float
let name = "Alice";   // Inferred as String
let flag = true;      // Inferred as Bool
let items = [1, 2];   // Inferred as Array

Type Compatibility

Numeric Types

let i: Int = 42;
let f: Float = 3.14;

// Int can be used where Float expected (auto-promotion)
let sum: Float = i + f;  // 45.14

// Float to Int requires explicit conversion
let rounded: Int = conv.to_int(f);

String Concatenation

Any type can be concatenated with String:

let message = "Count: " + 42;
let info = "Pi is " + 3.14;
let status = "Active: " + true;

Type System Summary

TypeExampleNotes
Int4264-bit signed
Float3.1464-bit IEEE 754
String"Hello"UTF-8
Booltrue
Bytesb"data"raw binary buffer
ErrorError("msg")first-class error
Nullnullabsence of value
Void—function return type only
Any—dynamic/untyped hint
Array[1, 2, 3]
Map{"key": "value"}
Struct(Name)Point { x: 1.0, y: 2.0 }user-defined
Function|x| { ... }first-class callable
Optional(T)T?nullable hint

Best Practices

1. Use Type Hints for Function Signatures

// Good: Clear interface
fn calculate(x: Int, y: Int) -> Int {
    return x + y;
}

// Okay: Less clear
fn calculate(x, y) {
    return x + y;
}

2. Be Consistent with Types

// Good: Consistent types
let numbers = [1, 2, 3, 4, 5];

// Avoid: Mixed types (unless necessary)
let mixed = [1, "two", 3.0];

3. Check for Null

if value != null {
    // Safe to use value
    io.print(value);
}

4. Use Meaningful Type Names

// Good
let user_count: Int = 100;
let temperature: Float = 98.6;

// Less clear
let x: Int = 100;
let y: Float = 98.6;

Type Errors

Common type-related errors:

// Division by zero
let result = 10 / 0;  // Runtime error

// Invalid conversion
let num = conv.to_int("abc");  // Runtime error

// Null access
let value = null;
io.print(value.field);  // Runtime error

Next Steps