Ovum has a rich type system with primitive types and user-defined types. The type system is static and does not permit implicit type coercions (an Int won’t automatically become a Float without an explicit cast, for example).
Fundamental types are passed by value and represent the basic building blocks of the language:
int (8 bytes) - 64-bit signed integer
    42, -17, 0x1A (hex), 0b1010 (binary)float (8 bytes) - 64-bit floating-point number (IEEE 754 double precision)
    3.14, 2.0e10, 1.5E-3, .5, 5.Infinity, -Infinity, NaNbyte (1 byte) - 8-bit unsigned integer
    255, 0x00, 0b11111111char - single Unicode character (UTF-32)
    'A', '中', '\n', '\t', '\0'bool - Boolean value (true, false)
    boolpointer - raw memory address (only meaningful in unsafe code)
    Fundamental Type Constraints: Fundamental types cannot be made nullable (
int?is invalid). They are notObjecttypes and cannot be stored inObjectArrayor cast toObject. To convert nullable primitives to fundamentals, cast to primitive first:(nullableInt as Int) as int.
Primitive reference types are built-in reference wrappers around fundamental types, passed by reference:
Int - reference wrapper for int valuesFloat - reference wrapper for float valuesByte - reference wrapper for byte valuesChar - reference wrapper for char valuesBool - reference wrapper for bool valuesPointer - reference wrapper for pointer values (only meaningful in unsafe code)Nullable Primitives: Any primitive reference type can be made nullable by appending
?(e.g.,Int?,Float?,Bool?).
String - immutable text data (UTF-8 encoded)
    "Hello", "Multi-line\nstring", "" (empty string)"Hello" + " " + "World"Object - root of all reference types
    Ovum provides specialized array classes for different element types (no generics/templates):
Primitive Arrays:
IntArray - array of Int reference wrappersFloatArray - array of Float reference wrappersBoolArray - array of Bool reference wrappersCharArray - array of Char reference wrappersByteArray - array of Byte reference wrappersPointerArray - array of Pointer reference wrappersObject Arrays:
ObjectArray - array of any Object-derived typesStringArray - convenience array of String (used for Main function arguments)Array Creation:
val numbers: IntArray = IntArray(10)        // Create array of Int reference wrappers
val names: StringArray = StringArray(5)     // Create string array of size 5
val objects: ObjectArray = ObjectArray(3)   // Create object array of size 3
Create type aliases for better code readability:
typealias UserId = Int
typealias UserName = String
fun ProcessUser(id: UserId, name: UserName): Void {
    // Implementation
}
=)The standard assignment operator assigns references for reference types:
val original: String = "Hello"
val reference: String = original  // Both variables point to the same string
:=)The copy assignment operator performs deep copy for reference types:
val original: String = "Hello"
val copy: String := original  // Creates a new string with the same content
Use the as operator for explicit casting:
val intValue: int = 42
val floatValue: float = (intValue as float)  // int to float
val floatNum: float = 3.14
val intNum: int = (floatNum as int)  // float to int (truncates)
// Implicit bidirectional casting between fundamental and primitive reference types
val fundamentalInt: int = 42
val primitiveInt: Int = fundamentalInt  // Implicit: int -> Int
val backToFundamental: int = primitiveInt  // Implicit: Int -> int
// Implicit conversion from literals to primitive types
val count: Int = 0  // Implicit: int literal -> Int
val flag: Bool = true  // Implicit: bool literal -> Bool
val pi: Float = 3.14  // Implicit: float literal -> Float
// Arithmetic works seamlessly
val sum: Int = 10 + 20  // Int + Int = Int (implicit conversion from literals)
val result: int = sum + 5  // Int + int = int (implicit conversion)
Any value can be explicitly cast to bool:
val intVal: int = 42
val boolVal: bool = (intVal as bool)  // true (non-zero)
val zeroInt: int = 0
val falseBool: bool = (zeroInt as bool)  // false (zero)
val nullString: String? = null
val nullBool: bool = (nullString as bool)  // false (null)
// With primitive reference types (implicit conversion)
val primitiveInt: Int = 42  // Implicit conversion from literal
val primitiveBool: bool = primitiveInt  // Implicit: Int -> bool
val boolRef: Bool = true  // Implicit: bool literal -> Bool
Rules: Fundamentals and primitive reference types: zero → false, non-zero → true.
References: null → false, non-null → true
Some casts require unsafe blocks:
unsafe {
    val obj: Object = Point(10, 20)
    val bytes: ByteArray = (obj as ByteArray)           // Raw byte view
    val mutableBytes: ByteArray = (obj as var ByteArray) // Mutable byte view
}
Fundamental types (int, float, byte, char, bool, pointer) are passed by value (copied):
fun ModifyInt(x: int): Void {
    x = x + 1  // Only modifies the local copy
}
Primitive reference types (Int, Float, Byte, Char, Bool, Pointer) and all other reference types (including String, arrays, and user-defined types) are passed by reference:
fun ModifyIntRef(var x: Int): Void {
    x = x + 1  // Implicit conversion: Int + int -> Int
}
fun ModifyArray(arr: IntArray): Void {
    arr[0] := Int(999)  // Use := for deep copy assignment
}
Immutability: References are immutable by default - use var for mutable references:
fun CannotReassign(str: String): Void {
    // str = "New value"  // ERROR: Cannot reassign immutable reference
}
fun CanReassign(var str: String): Void {
    str = "New value"  // OK: str is mutable
}
Static typing: Every variable and expression has a type checked at compile time
Limited implicit conversions: The compiler only performs implicit conversions between a primitive reference type and its matching fundamental (for example, Int ↔ int). Any conversion across different primitive families—such as Int to Float or Float to int—must use an explicit cast.
Type safety: Prevents many common errors
Nullable types: Any reference type (including primitive reference types) can be made nullable by appending ?. Fundamental types cannot be nullable.
val x: int = 42
val y: String = "Hello"
// val z: int = x + y  // ERROR: Cannot add int and String
val intVal: int = 42
val floatVal: float = 3.14
val result: float = (intVal as float) + floatVal  // OK: Explicit conversion
// Using primitive reference types (implicit conversion between wrappers and fundamentals)
val refInt: Int = 42  // Implicit conversion from literal to Int
val refFloat: Float = 3.14  // Implicit conversion from literal to Float
val sum: Int = refInt + (refFloat as Int)  // Requires explicit narrowing
val fundamentalSum: int = sum + 10  // Implicit: Int -> int when assigning to a fundamental
// Converting nullable primitives to fundamentals
val nullableInt: Int? = 42  // Implicit conversion from literal
val fundamentalFromNullable: int = (nullableInt ?: 0) as int  // Two-step conversion
Types used as parameters in pure functions must implement IComparable for stable ordering:
pure fun ProcessData(data: IComparable): Int {
    // data must implement IComparable for stable ordering
    return data.GetHash()
}
Type information is preserved at runtime for reference types:
fun ProcessObject(obj: Object): Void {
    if (obj is String) {
        val str: String? = obj as String
        if (str != null) {
            val nonNullStr: String = str ?: "default"  // Use Elvis operator
            sys::Print("String length: " + nonNullStr.Length().ToString())
        }
    } else if (obj is IntArray) {
        val arr: IntArray? = obj as IntArray
        if (arr != null) {
            val nonNullArr: IntArray = arr ?: IntArray(0)  // Use Elvis operator
            sys::Print("Array size: " + nonNullArr.Length().ToString())
        }
    } else if (obj is Int) {
        val intRef: Int? = obj as Int
        if (intRef != null) {
            val nonNullInt: Int = intRef ?: 0  // Use Elvis operator
            sys::Print("Int value: " + nonNullInt.ToString())  // Implicit conversion to string
        }
    }
}