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