Variables and Types¶
Onion is a statically-typed language, meaning every variable has a type determined at compile time.
Type Annotations¶
Local val / var declarations can omit the type when an initializer is present. Use explicit type annotations for fields, top-level declarations, or when no initializer is provided:
Type System¶
Primitive Types¶
Onion supports all JVM primitive types:
val byteVal: Byte = 127
val shortVal: Short = 32767
val intVal: Int = 2147483647
val longVal: Long = 9223372036854775807L
val floatVal: Float = 3.14f
val doubleVal: Double = 3.14159
val charVal: Char = 'A'
val boolVal: Boolean = true
Reference Types¶
String Type¶
val message: String = "Hello, World!"
val empty: String = ""
val multiline: String = "Line 1\nLine 2"
Class Types¶
Any Java or Onion class can be used as a type:
import {
java.util.ArrayList;
java.io.File;
}
val list: ArrayList[String] = new ArrayList[String]()
val file: File = new File("data.txt")
Interface Types¶
import {
java.util.List;
java.util.ArrayList;
}
val list: List[String] = new ArrayList[String]() // Interface type
Array Types¶
Arrays are declared with Type[] syntax:
val integers: Int[] = new Int[10]
val strings: String[] = new String[3]
strings[0] = "a"
strings[1] = "b"
strings[2] = "c"
val objects: Object[] = new Object[5]
Null Type and Nullable Types¶
The null literal can be assigned to reference types. For better null safety, use nullable types (T?):
// Traditional (allows null implicitly)
val maybeString: String = null
val maybeObject: Object = null
// Recommended: Explicit nullable types
val safeName: String? = null // Clearly nullable
val definite: String = "hello" // Cannot be null
val upper: Object? = safeName?.toUpperCase() // Safe call
See Null Safety for detailed information about nullable types and the safe call operator (?.).
Bottom Type (Nothing)¶
Nothing is the subtype of all types and is used for expressions that never return, such as return, throw, break, and continue.
Type Casting¶
Using the as Operator¶
Convert between types using the as cast operator:
// Numeric casting
val x: Double = 3.14
val y: Int = (x as Int) // 3
// Object casting
val obj: Object = "Hello"
val str: String = (obj as String)
// Random number to Int
val random: Int = (Math::random() * 100) as Int
Automatic Widening¶
Smaller numeric types automatically widen to larger ones:
val i: Int = 42
val l: Long = i // Int → Long (automatic)
val d: Double = l // Long → Double (automatic)
Explicit Narrowing¶
Narrowing conversions require explicit casting:
Type Compatibility¶
Assignment Compatibility¶
A value can be assigned to a variable if:
- Types are exactly the same
- Value type is a subtype of variable type
- Automatic widening applies (for primitives)
import { java.util.ArrayList; java.util.List; }
// Same type
val s1: String = "Hello"
val s2: String = s1 // OK
// Subtype
val arrayList: ArrayList[String] = new ArrayList[String]()
val list: List[String] = arrayList // OK (ArrayList implements List)
// Widening
val i: Int = 42
val l: Long = i // OK (Int → Long)
Variable Scope¶
Local Variables¶
Variables declared in methods or blocks:
def method {
val local: Int = 10
if true {
val nested: Int = 20
println(local) // OK
println(nested) // OK
}
// println(nested) // ERROR: nested not in scope
}
Fields¶
Declare fields with val / var and access them via this.field:
class Counter {
var count: Int
public:
def this {
this.count = 0 // Initialize member
}
def increment {
this.count = this.count + 1 // Access member
}
def getCount: Int = this.count // Return member value
}
Static Variables¶
Static members belong to the class, not instances:
class MathUtils {
static val PI: Double = 3.14159
public:
static def square(x: Double): Double = x * x
}
// Access static members
val pi: Double = MathUtils::PI
val result: Double = MathUtils::square(5.0)
Type Inference¶
Onion can infer the type of local val / var declarations when an initializer is present (fields and top-level declarations still require explicit types):
// With explicit type
val name: String = "Alice"
// Inferred from the initializer
val age = 30 // Int
var greeting = "Hi" // String
Destructuring Declarations¶
val (a, b) = expr binds names to the positional components of a record
(or a Map.Entry via getKey/getValue). var makes the bindings mutable.
The initializer is evaluated once; arity mismatches are compile errors.
record Point(x: Int, y: Int)
val (a, b) = new Point(1, 2) // a = 1, b = 2
var (mx, my) = new Point(7, 8)
mx = mx + 1
Generic Types (Java Generics)¶
When using Java generic types, specify type parameters with []:
import {
java.util.ArrayList;
java.util.HashMap;
}
val list: ArrayList[String] = new ArrayList[String]
val map: HashMap[String, Int] = new HashMap[String, Int]
Primitive Type Arguments¶
Type arguments may be primitive types (e.g., Int). Onion uses JVM erasure, so primitive type arguments are boxed/unboxed at call boundaries:
Invariance of Type Arguments¶
Type arguments are invariant: Box[Dog] is not assignable to
Box[Animal] even though Dog is a subtype of Animal. This prevents heap
pollution (a Box[Animal] view could otherwise store a non-Dog). Only an
identical parameterization is compatible:
class Animal { public: def this {} }
class Dog : Animal { public: def this {} }
class Box[T] {
val v: T
public:
def this(x: T) { v = x }
def get(): T = v
}
val bd: Box[Dog] = new Box(new Dog())
val same: Box[Dog] = bd // OK: identical parameterization
// val wide: Box[Animal] = bd // ERROR E0000: Box[Dog] is not a Box[Animal]
Invariance applies to the type arguments; the generic class itself still
participates in normal subtyping (e.g., ArrayList[String] is a
List[String]).
Function Types¶
Functions are represented by Function0 through Function10 interfaces:
When the target function type is known, lambda parameters can omit types. If no explicit function type is provided, the return type is inferred from the body.
Function0- No parametersFunction1- One parameterFunction2- Two parameters- ... up to
Function10
Next Steps¶
- Null Safety - Nullable types and safe call operator
- Control Flow - if, while, for, select
- Functions - Function definitions and lambdas
- Classes and Objects - Object-oriented programming