Object-Oriented Programming
Coco takes a pragmatic approach to OOP—composition over inheritance, traits for behavior, and types that are simple yet powerful.
What's in This Chapter
We'll cover Coco's type system for OOP:
- Types and Methods — Defining types and attaching behavior
- Constructors — Creating instances with static methods and factories
- Composition — Embedding types and method forwarding
- Traits — Shared behavior and default implementations
- Polymorphism — Generics with traits and dynamic dispatch
- Trait Objects — Runtime polymorphism and type erasure
- Magic Methods — Automatic lifecycle hooks and operator overloading
Design Philosophy
Coco's OOP is guided by hard-earned lessons from other languages:
Composition over inheritance. Deep inheritance hierarchies are hard to understand and change. Coco uses composition—embed what you need, delegate to it explicitly.
Traits for shared behavior. Instead of inheriting implementation, types implement traits that define what they can do. A Dog doesn't inherit from Animal—it implements Speaker.
Explicit is better than implicit. No hidden vtables, no surprise method resolution. You always know where a method comes from.
OOP is a tool, not a religion. Use it when it fits. Many problems are better solved with functions and data.
Quick Examples
// Define a type with methods inside
type Point {
x i32
y i32
fn distance: f64 {
$dx = $this.x as f64;
$dy = $this.y as f64;
return ($dx $dx + $dy $dy).sqrt();
}
fn new($x i32, $y i32): Point {
return Point { x: $x, y: $y };
}
}
// Usage
$p = Point::new(3, 4);
echo $p.distance(); // 5.0
// Traits for shared behavior
trait Drawable {
fn draw;
}
type Circle {
radius f64
@Drawable
fn draw {
echo "Drawing circle with radius {$this.radius}";
}
}
// Polymorphism
fn render<T: Drawable>($item T) {
$item.draw();
}
When to Use OOP
OOP works well for:
- Entities with state and behavior — Users, connections, UI widgets
- Encapsulating complexity — Database pools, HTTP clients
- Polymorphic collections — Different shapes that can all be drawn
Consider simpler approaches for:
- Pure data — Use plain types without methods
- Stateless transformations — Use functions
- One-off operations — Don't create types just to hold methods
Let's dive into the details.
Pages in this chapter:
- Types and Methods — Defining custom types and attaching methods
- Constructors — Creating instances with static methods and factory patterns
- Composition — Embedding types and building complex structures from simple ones
- Traits — Defining shared behavior with traits and default implementations
- Polymorphism — Static and dynamic polymorphism with generics and trait objects
- Trait Objects — Runtime polymorphism through type erasure and dynamic dispatch
- Magic Methods — Special methods that are called automatically for lifecycle events, property access, and operator overloading