Object-Oriented Programming
Classes, objects, methods, and inheritance in Strada.
Overview
Strada's OOP model is inspired by Perl:
- Classes are defined using
package - Objects are blessed hash references
- Methods are functions that take
$selfas the first parameter - Automatic method registration enables
$obj->method()syntax
Defining a Class
package Dog;
# Constructor
func new(str $name, int $age) scalar {
my hash %self = {
"name" => $name,
"age" => $age
};
return bless(\%self, "Dog");
}
# Method
func speak(scalar $self) void {
say($self->{"name"} . " says woof!");
}
# Getter method
func get_name(scalar $self) str {
return $self->{"name"};
}
# Setter method
func set_age(scalar $self, int $age) void {
$self->{"age"} = $age;
}
Creating and Using Objects
func main() int {
# Create object using constructor
my scalar $dog = Dog::new("Rex", 3);
# Call methods with arrow syntax
$dog->speak(); # "Rex says woof!"
# Access properties
my str $name = $dog->get_name();
say("Dog's name: " . $name);
# Modify properties
$dog->set_age(4);
return 0;
}
The bless() Function
bless() associates a hash reference with a class name, turning it into an object:
# bless(reference, classname) -> object
my hash %data = { "x" => 10 };
my scalar $obj = bless(\%data, "MyClass");
Package Auto-Prefixing
Functions inside a package block are automatically prefixed with the class name:
package Animal;
# This becomes Animal_new
func new(str $name) scalar {
my hash %self = { "name" => $name };
return bless(\%self, "Animal");
}
# This becomes Animal_speak
func speak(scalar $self) void {
say($self->{"name"} . " makes a sound");
}
# Call with either syntax:
# Animal::new("Rex") or $obj->speak()
Auto-Prefix Rules
- Only applies to simple packages (no
::in name) - Skips
mainfunction andpackage main; - Skips functions that already look class-prefixed (e.g.,
Dog_speak) externfunctions are NOT auto-prefixed
Inheritance
Implement inheritance by blessing with a derived class name and calling parent methods:
package Animal;
func new(str $name) scalar {
my hash %self = { "name" => $name };
return bless(\%self, "Animal");
}
func speak(scalar $self) void {
say($self->{"name"} . " makes a sound");
}
package Dog;
func new(str $name, str $breed) scalar {
my hash %self = {
"name" => $name,
"breed" => $breed
};
return bless(\%self, "Dog");
}
# Override parent method
func speak(scalar $self) void {
say($self->{"name"} . " barks: Woof!");
}
func get_breed(scalar $self) str {
return $self->{"breed"};
}
package Cat;
func new(str $name) scalar {
my hash %self = { "name" => $name };
return bless(\%self, "Cat");
}
func speak(scalar $self) void {
say($self->{"name"} . " meows: Meow!");
}
UNIVERSAL Methods
All objects have access to isa() and can() methods:
my scalar $dog = Dog::new("Rex", "German Shepherd");
# Check if object is of a class
if ($dog->isa("Dog")) {
say("It's a Dog!");
}
# Check if object has a method
if ($dog->can("speak")) {
$dog->speak();
}
if ($dog->can("fly")) {
say("Dog can fly");
} else {
say("Dogs cannot fly");
}
Polymorphism
func make_sound(scalar $animal) void {
# Polymorphic method call
$animal->speak();
}
func main() int {
my scalar $dog = Dog::new("Rex", "Lab");
my scalar $cat = Cat::new("Whiskers");
make_sound($dog); # "Rex barks: Woof!"
make_sound($cat); # "Whiskers meows: Meow!"
# Store in array for iteration
my array @animals = [$dog, $cat];
foreach my scalar $animal (@animals) {
$animal->speak();
}
return 0;
}
Function Pointers in Objects
Objects can store function references as properties:
package Button;
func new(str $label, scalar $on_click) scalar {
my hash %self = {
"label" => $label,
"on_click" => $on_click
};
return bless(\%self, "Button");
}
func click(scalar $self) void {
my scalar $handler = $self->{"on_click"};
$handler->();
}
# Usage
func main() int {
my scalar $btn = Button::new("Submit", func () {
say("Button clicked!");
});
$btn->click(); # "Button clicked!"
return 0;
}
Method Chaining
Return $self from methods to enable chaining:
package Builder;
func new() scalar {
my hash %self = { "parts" => [] };
return bless(\%self, "Builder");
}
func add(scalar $self, str $part) scalar {
push($self->{"parts"}, $part);
return $self; # Return self for chaining
}
func build(scalar $self) str {
return join(", ", $self->{"parts"});
}
# Usage with chaining
func main() int {
my str $result = Builder::new()
->add("header")
->add("body")
->add("footer")
->build();
say($result); # "header, body, footer"
return 0;
}
Best Practices
OOP Tips
- Always use
packageto define classes - Name constructors
newby convention - First parameter of methods should be
scalar $self - Store instance data in the blessed hash
- Use getters/setters for controlled access to properties
- Return
$selffrom setters for method chaining