Language Overview¶
Onion is a statically-typed, object-oriented programming language designed for the Java Virtual Machine (JVM). This page provides an overview of the language's philosophy, design goals, and key characteristics.
Design Philosophy¶
Onion was created with several goals in mind:
- Static Type Safety - Catch errors at compile time while maintaining expressiveness
- Java Interoperability - Seamless integration with existing Java libraries and frameworks
- Concise Syntax - Reduce boilerplate while keeping code readable
- Familiar Concepts - Build on established OOP and functional programming patterns
- JVM Performance - Leverage the mature JVM ecosystem and runtime optimizations
Language Characteristics¶
Statically Typed¶
Every variable and expression has a type known at compile time:
The type system includes:
- Primitive types: Int, Long, Double, Float, Boolean, Byte, Short, Char
- Reference types: Classes and interfaces
- Array types: Type[]
- Null type: Special handling for null values
- Bottom type: Nothing for non-returning expressions
Object-Oriented¶
Onion fully supports object-oriented programming:
class Animal {
val name: String
public:
def this(n: String) {
this.name = n
}
def speak: String {
return "Some sound"
}
}
class Dog : Animal {
public:
def this(n: String): (n) {
}
def speak: String {
return "Woof!"
}
}
Features: - Classes - Encapsulation of data and behavior - Inheritance - Single class inheritance, multiple interface implementation - Polymorphism - Method overriding and overloading - Access Control - Public/private visibility - Interfaces - Abstract contracts
Functional Elements¶
While primarily object-oriented, Onion includes functional programming features:
// Lambda expressions
val filter: (Int) -> Boolean = (x: Int) -> { return x > 10; }
// Closures
def makeCounter(): () -> Int {
var count: Int = 0
return () -> {
count = count + 1;
return count;
};
}
val counter: () -> Int = makeCounter()
println(counter.call()) // 1
println(counter.call()) // 2
Features:
- Lambda expressions - Anonymous functions with (params) -> { body } syntax
- Closures - Functions that capture variables from their enclosing scope
- First-class functions - Functions as values via Function0 through Function10 interfaces
JVM Target¶
Onion compiles directly to JVM bytecode:
- Compiled
.classfiles are standard JVM classes - Can be packaged in JARs alongside Java classes
- Inherits JVM's performance characteristics
- Access to the entire Java ecosystem
Java Interoperability¶
Direct, seamless access to Java:
import {
java.util.ArrayList;
java.util.HashMap;
javax.swing.JFrame;
}
val list: ArrayList[String] = new ArrayList[String]()
val map: HashMap[String, String] = new HashMap[String, String]()
val window: JFrame = new JFrame("Title")
Key points:
- Import Java classes with import { }
- Instantiate Java objects with new
- Call Java methods normally
- Implement Java interfaces
- Extend Java classes
- Use :: for static method access
Compilation Model¶
The Onion compiler follows a multi-phase architecture:
Source Code (.on)
↓
[Parsing] - JavaCC grammar → Untyped AST
↓
[Rewriting] - Normalization → Transformed AST
↓
[Type Checking] - Type inference & validation → Typed AST
↓
[Code Generation] - ASM bytecode generation → .class files
Compilation Modes¶
- File Compilation (
onionc) - Produces.classfiles - Script Execution (
onion) - Compiles to memory and runs immediately - Interactive REPL (
Shell) - Evaluate expressions interactively
Syntax Highlights¶
Fields with val / var¶
Declare fields with val (immutable) or var (mutable) and access them via this.field:
Type Annotations with :¶
Types are specified after a colon. Local declarations can omit the type when an initializer is present:
Static Access with ::¶
Static methods and fields use :::
Default static imports make some class members available without :: (for example, println("Hello") from onion.IO). The list lives in src/main/resources/onion/default-static-imports.txt.
Type Casting with as¶
Cast expressions use the as operator:
val x: Double = 3.14
val y: Int = (x as Int) // Cast to Int
val obj: Object = "string"
val str: String = (obj as String) // Cast to String
Pattern Matching with select¶
Switch-style pattern matching:
select value {
case 1, 2, 3:
println("Small")
case 4, 5, 6:
println("Medium")
else:
println("Large")
}
What's Different from Java?¶
| Feature | Java | Onion |
|---|---|---|
| Field declarations | Type field |
val/var field: Type |
| Variable declarations | Type variable |
val/var variable[: Type] = value |
| Static access | Class.method() |
Class::method() |
| Type casting | (Type) value |
value as Type |
| Lambda syntax | (x) -> x + 1 |
(x :Int) -> { return x + 1; } |
| Pattern matching | switch (Java 14+) |
select |
| List append | list.add(x) |
list << x |
Current Limitations¶
As documented in the README:
- Robustness - The compiler enforces a no-crash / no-miscompile bar via a mutation fuzzer, a crash-reproducer corpus, and codegen-correctness tests; if you do hit a crash or miscompilation, please file a minimal repro
- Erasure generics - No reified type info; type arguments are invariant (no variance or wildcards)
- Tail-call optimization - Covers direct and mutual self-recursion, not general continuation-passing style
- Diagnostics - Some errors are reported later in the pipeline than ideal
The examples in the run/ directory are verified to compile and execute correctly.
Next Steps¶
- Basic Syntax - Learn the fundamentals
- Classes and Objects - Object-oriented programming
- Java Interoperability - Working with Java libraries