Language Reference

Complete specification of the Charl programming language

Syntax

Lexical Structure

Charl uses a C-like syntax with Rust influences. Comments start with // for single-line and /* */ for multi-line.

// Single-line comment

/*
 * Multi-line comment
 * Can span multiple lines
 */

// Identifiers: alphanumeric + underscore, must start with letter or underscore
let my_variable = 42
let _private = "hidden"

// Keywords are reserved
// let, fn, if, else, for, while, match, return, true, false, null

Statements and Expressions

// Statements (don't return values)
let x = 42;

// Expressions (return values)
let y = if x > 0 { x } else { -x };  // Conditional expression
let z = { let a = 1; let b = 2; a + b };  // Block expression

// Function calls are expressions
let result = add(2, 3)

// Semicolons are optional at end of statements
let a = 10
let b = 20

Type System

Type Description Example
int32 32-bit signed integer 42, -100
int64 64-bit signed integer 1000000
float32 32-bit floating point 3.14f32
float64 64-bit floating point 3.14159
bool Boolean value true, false
string UTF-8 string "hello"
[T] Array of type T [1, 2, 3]
Tensor Multi-dimensional array Tensor::new([1, 2])

Type Inference

Charl has a powerful type inference system. Explicit type annotations are optional in most cases.

// Type inference
let x = 42              // Inferred as int32
let y = 3.14           // Inferred as float64
let name = "Charl"     // Inferred as string

// Explicit type annotations
let a: int32 = 100
let b: float64 = 2.5
let c: string = "explicit"

// Array type inference
let numbers = [1, 2, 3, 4, 5]           // [int32]
let floats: [float64] = [1.0, 2.0]     // Explicit array type

// Function return type inference
fn double(x: int32) {
    return x * 2  // Return type inferred as int32
}

Generic Types

// Generic function
fn identity(x: T) -> T {
    return x
}

let num = identity(42)        // T = int32
let text = identity("hello")  // T = string

// Generic with constraints
fn add(a: T, b: T) -> T {
    return a + b
}

// Generic structs (future feature)
struct Pair {
    first: T,
    second: U
}

Variables

Variable Declaration

// Immutable variables (default)
let x = 42
// x = 43  // Error: cannot assign to immutable variable

// Mutable variables
let mut y = 10
y = 20  // OK

// Multiple declarations
let a = 1, b = 2, c = 3

// Destructuring (future feature)
let (x, y) = (1, 2)
let [first, second, rest...] = [1, 2, 3, 4, 5]

Scope and Shadowing

let x = 10

{
    let x = 20  // Shadows outer x
    print(x)    // Prints: 20
}

print(x)  // Prints: 10

// Shadowing with different types
let value = "42"
let value = 42  // OK: shadows with different type

Functions

Function Declaration

// Basic function
fn add(a: int32, b: int32) -> int32 {
    return a + b
}

// Function with inferred return type
fn multiply(x: int32, y: int32) {
    return x * y  // Return type inferred
}

// No parameters
fn greet() {
    print("Hello!")
}

// Multiple return values (via tuple)
fn divide(a: int32, b: int32) -> (int32, int32) {
    let quotient = a / b
    let remainder = a % b
    return (quotient, remainder)
}

Higher-Order Functions

// Function as parameter
fn apply(f: fn(int32) -> int32, x: int32) -> int32 {
    return f(x)
}

fn square(n: int32) -> int32 {
    return n * n
}

let result = apply(square, 5)  // 25

// Function returning function
fn make_adder(n: int32) -> fn(int32) -> int32 {
    return |x| { x + n }
}

let add_10 = make_adder(10)
print(add_10(5))  // 15

Closures

// Lambda expressions
let add = |a, b| { a + b }
let square = |x| { x * x }

// Capturing environment
let factor = 10
let multiply_by_factor = |x| { x * factor }

print(multiply_by_factor(5))  // 50

// Short form for single expression
let double = |x| x * 2

// Type annotations in closures
let typed = |x: int32, y: int32| -> int32 { x + y }

Control Flow

If-Else

// If statement
if x > 0 {
    print("positive")
}

// If-else
if x > 0 {
    print("positive")
} else {
    print("non-positive")
}

// If-else if-else chain
if x > 0 {
    print("positive")
} else if x < 0 {
    print("negative")
} else {
    print("zero")
}

// If as expression
let abs = if x >= 0 { x } else { -x }

Loops

// For loop with range
for i in range(10) {
    print(i)  // 0 to 9
}

// For loop with array
let numbers = [1, 2, 3, 4, 5]
for num in numbers {
    print(num)
}

// While loop
let mut counter = 0
while counter < 10 {
    print(counter)
    counter = counter + 1
}

// Infinite loop with break
let mut i = 0
loop {
    if i >= 10 {
        break
    }
    print(i)
    i = i + 1
}

// Continue statement
for i in range(10) {
    if i % 2 == 0 {
        continue
    }
    print(i)  // Only odd numbers
}

Operators

Category Operators Description
Arithmetic + - * / % Addition, subtraction, multiplication, division, modulo
Comparison == != < > <= >= Equality, inequality, less/greater than
Logical && || ! Logical AND, OR, NOT
Bitwise & | ^ << >> Bitwise AND, OR, XOR, left shift, right shift
Assignment = += -= *= /= Assignment and compound assignment
Matrix @ Matrix multiplication

Operator Examples

// Arithmetic
let sum = 10 + 5
let diff = 10 - 5
let prod = 10 * 5
let quot = 10 / 5
let rem = 10 % 3

// Comparison
let equal = (5 == 5)       // true
let not_equal = (5 != 3)   // true
let less = (3 < 5)         // true

// Logical
let and_result = true && false   // false
let or_result = true || false    // true
let not_result = !true           // false

// Matrix operations
let a = Tensor::randn([10, 20])
let b = Tensor::randn([20, 30])
let c = a @ b  // Matrix multiplication: [10, 30]

Pattern Matching

Match Expression

// Basic pattern matching
let x = 42

match x {
    0 => print("zero"),
    1 => print("one"),
    2 => print("two"),
    _ => print("other")
}

// Match as expression
let description = match x {
    0 => "zero",
    1 => "one",
    2 => "two",
    _ => "other"
}

// Range patterns
match age {
    0..=12 => print("child"),
    13..=19 => print("teenager"),
    20..=64 => print("adult"),
    _ => print("senior")
}

// Multiple patterns
match day {
    "Saturday" | "Sunday" => print("weekend"),
    _ => print("weekday")
}

Guards

// Pattern matching with guards
match value {
    x if x < 0 => print("negative"),
    x if x == 0 => print("zero"),
    x if x > 0 => print("positive"),
    _ => print("unknown")
}

// Complex guards
match point {
    (x, y) if x == y => print("on diagonal"),
    (0, y) => print("on y-axis"),
    (x, 0) => print("on x-axis"),
    _ => print("elsewhere")
}

Modules

Module Declaration

// module.ch
module math {
    fn add(a: int32, b: int32) -> int32 {
        return a + b
    }

    fn multiply(a: int32, b: int32) -> int32 {
        return a * b
    }
}

// Using module
let result = math::add(5, 3)

Imports

// Import entire module
use std::math

// Import specific items
use std::collections::{HashMap, HashSet}

// Import with alias
use std::io as io_utils

// Glob import
use std::prelude::*

Tensors

Tensor Creation

// From array
let t1 = Tensor::new([1.0, 2.0, 3.0])

// With specific shape
let t2 = Tensor::zeros([3, 4])        // 3x4 tensor of zeros
let t3 = Tensor::ones([2, 3, 4])      // 2x3x4 tensor of ones
let t4 = Tensor::randn([10, 10])      // Random normal distribution

// With gradient tracking
let x = Tensor::new([2.0, 3.0], requires_grad: true)
let y = Tensor::randn([5, 5], requires_grad: true)

// Device placement
let gpu_tensor = Tensor::zeros([1000, 1000], device: "gpu")
let cpu_tensor = Tensor::ones([100, 100], device: "cpu")

Tensor Operations

let a = Tensor::new([1.0, 2.0, 3.0])
let b = Tensor::new([4.0, 5.0, 6.0])

// Element-wise operations
let sum = a + b
let diff = a - b
let prod = a * b
let quot = a / b

// Matrix operations
let m1 = Tensor::randn([10, 20])
let m2 = Tensor::randn([20, 30])
let result = m1 @ m2  // Matrix multiplication

// Reduction operations
let total = a.sum()
let mean = a.mean()
let max = a.max()
let min = a.min()

// Reshaping
let reshaped = a.reshape([3, 1])
let transposed = m1.transpose()

// Slicing
let slice = m1[0:5, 10:15]  // Rows 0-4, columns 10-14