$CLASS Type Reference
Class System: Grapa provides a sophisticated object-oriented programming system with classes, inheritance, and copy-on-write semantics for instance variables.
Table of Contents
- Class Definition
- Class Inheritance
- Instance Creation
- Variable Scoping and Copy-on-Write
- Method Inheritance
- Inheritance Precedence
- System Classes vs User Classes
- Examples
Class Definition
Classes in Grapa are defined using the class
keyword with optional inheritance:
/* Basic class definition */
MyClass = class ($OBJ) {
/* Class-level variables */
i = 1;
name = "default";
/* Class-level methods */
f = op() { i; };
greet = op() { "Hello, " + name; };
};
/* Class without inheritance */
SimpleClass = class {
value = 42;
getValue = op() { value; };
};
Key Features: - Class-level variables: Shared by all instances until modified - Class-level methods: Defined once and inherited by all instances - Optional inheritance: Can inherit from other classes (including system classes) - Dynamic modification: Classes can be modified at runtime
Class Inheritance
Grapa supports single and multiple inheritance:
/* Single inheritance */
BaseClass = class ($OBJ) {
baseValue = 10;
baseMethod = op() { "Base: " + baseValue; };
};
DerivedClass = class (BaseClass) {
derivedValue = 20;
derivedMethod = op() { "Derived: " + derivedValue; };
};
/* Multiple inheritance */
Mixin1 = class {
mixin1Method = op() { "Mixin1"; };
};
Mixin2 = class {
mixin2Method = op() { "Mixin2"; };
};
MultiClass = class (BaseClass, Mixin1, Mixin2) {
multiValue = 30;
};
Inheritance Rules:
- Method Resolution Order: Closer classes in inheritance chain override more distant ones
- Variable Inheritance: All class-level variables are inherited
- Method Inheritance: All methods are inherited and can be overridden
- System Class Inheritance: Can inherit from system classes like $OBJ
, $ARRAY
, etc.
Instance Creation
Instances are created by calling the class as a function:
/* Create instances */
obj1 = MyClass();
obj2 = MyClass();
derived = DerivedClass();
multi = MultiClass();
/* Check instance types */
obj1.type(); /* Returns: "MyClass" */
derived.type(); /* Returns: "DerivedClass" */
multi.type(); /* Returns: "MultiClass" */
Instance Properties:
- Type: Instance type matches the class name
- Initial State: Initially empty {}
- uses class variables
- Independent: Each instance is independent of others
- Class Reference: Maintains reference to class definition
Variable Scoping and Copy-on-Write
Grapa implements sophisticated copy-on-write semantics for instance variables:
/* Class definition */
MyClass = class ($OBJ) {
i = 1; /* Class-level variable */
f = op() { i; }; /* Class-level method */
};
/* Create instance */
x = MyClass();
/* Initially uses class variable */
x.f(); /* Returns: 1 (from class) */
x; /* Returns: {} (no instance variables yet) */
/* Modifying creates instance copy */
x.i += 1; /* Copies i to instance, then increments */
/* Now uses instance variable */
x.f(); /* Returns: 2 (from instance) */
x; /* Returns: {"i": 2} (instance variable created) */
/* Class definition unchanged */
MyClass; /* Still shows: class ($OBJ) {i = 1; f = @<[op,@<var,{i}>],{}>; }; */
Copy-on-Write Behavior: - Shared by Default: All instances share class-level variables - Copy on Modification: Instance variables created only when modified - Method Context: Methods automatically use appropriate variable context - Memory Efficiency: Unmodified variables remain shared at class level - Class Isolation: Class definition remains unchanged
Method Inheritance
Methods inherit from the class definition but access the appropriate variable context:
/* Class with methods */
Counter = class ($OBJ) {
count = 0;
increment = op() { count += 1; };
getCount = op() { count; };
reset = op() { count = 0; };
};
/* Create instances */
counter1 = Counter();
counter2 = Counter();
/* Both use class method but different instance variables */
counter1.increment(); /* Uses class method, creates instance count */
counter1.increment(); /* Uses class method, modifies instance count */
counter1.getCount(); /* Returns: 2 (from instance) */
counter2.increment(); /* Uses class method, creates separate instance count */
counter2.getCount(); /* Returns: 1 (from separate instance) */
/* Class method definition unchanged */
Counter; /* Method definitions remain in class */
Method Inheritance Features: - Shared Definition: All instances use the same method definition - Instance Context: Methods access instance variables when they exist - Class Context: Methods fall back to class variables when instance variables don't exist - Dynamic Resolution: Variable resolution happens at method execution time
Inheritance Precedence
When multiple classes are inherited, closer classes in the inheritance chain take precedence:
/* Base class */
Base = class ($OBJ) {
value = "base";
method = op() { "Base method: " + value; };
};
/* Intermediate class */
Intermediate = class (Base) {
value = "intermediate";
method = op() { "Intermediate method: " + value; };
};
/* Derived class */
Derived = class (Intermediate) {
value = "derived";
method = op() { "Derived method: " + value; };
};
/* Create instance */
obj = Derived();
/* Method resolution follows inheritance chain */
obj.method(); /* Returns: "Derived method: derived" */
obj.value; /* Returns: "derived" (from closest class) */
Precedence Rules: - Closest First: Classes closer to the instance override more distant ones - Left to Right: In multiple inheritance, leftmost classes take precedence - Method Override: Methods can be overridden at any level - Variable Override: Variables can be overridden at any level
System Classes vs User Classes
Grapa distinguishes between system classes (names starting with "$") and user classes:
/* User class - inherits full $OBJ functionality */
MyClass = class ($OBJ) {
data = "user data";
getData = op() { data; };
};
obj = MyClass();
obj.set("key", "value"); /* Works - has universal .set()/.get() methods */
obj.getfield("key", "value"); /* Works - has flexible .getfield()/.setfield() methods */
/* System classes have specialized implementations */
file = $file();
file.setfield("key", "value"); /* Works - specialized file system operations */
/* file.setfield(0, "value"); /* Would NOT work - no index support */
table = {}.table("ROW");
table.setfield("key", "field", "value"); /* Works - specialized database operations */
/* table.setfield(0, "field", "value"); /* Would NOT work - no index support */
System Class Characteristics:
- Names start with "$": $file
, $TABLE
, $OBJ
, etc.
- Specialized implementations: Override universal methods for specific data structures
- C++ routing: System classes bypass universal $OBJ
processing
- Limited flexibility: May not support all universal method features
User Class Characteristics:
- Names don't start with "$": MyClass
, Counter
, etc.
- Universal functionality: Inherit full $OBJ
method capabilities
- Flexible methods: Support mix/match of names and indices
- Standard inheritance: Use normal class inheritance mechanisms
Examples
Complete Class System Example
/* Base class for all vehicles */
Vehicle = class ($OBJ) {
wheels = 4;
speed = 0;
maxSpeed = 100;
accelerate = op(amount) {
speed += amount;
if (speed > maxSpeed) {
speed = maxSpeed;
};
speed;
};
brake = op(amount) {
speed -= amount;
if (speed < 0) {
speed = 0;
};
speed;
};
getInfo = op() {
"Vehicle: " + wheels + " wheels, speed: " + speed + "/" + maxSpeed;
};
};
/* Car inherits from Vehicle */
Car = class (Vehicle) {
doors = 4;
fuel = 100;
refuel = op(amount) {
fuel += amount;
if (fuel > 100) {
fuel = 100;
};
fuel;
};
getInfo = op() {
Vehicle.getInfo() + ", doors: " + doors + ", fuel: " + fuel + "%";
};
};
/* Motorcycle inherits from Vehicle */
Motorcycle = class (Vehicle) {
wheels = 2;
doors = 0;
maxSpeed = 150;
wheelie = op() {
if (speed > 20) {
"Doing a wheelie at " + speed + " mph!";
} else {
"Need more speed for a wheelie!";
};
};
};
/* Create instances */
myCar = Car();
myBike = Motorcycle();
/* Demonstrate inheritance and copy-on-write */
myCar.accelerate(50); /* Uses inherited method, creates instance speed */
myCar.refuel(25); /* Uses Car method, creates instance fuel */
myCar.getInfo(); /* Returns: "Vehicle: 4 wheels, speed: 50/100, doors: 4, fuel: 125%" */
myBike.accelerate(80); /* Uses inherited method, creates separate instance speed */
myBike.wheelie(); /* Uses Motorcycle method */
myBike.getInfo(); /* Returns: "Vehicle: 2 wheels, speed: 80/150" */
/* Class definitions unchanged */
Vehicle; /* Original definition preserved */
Car; /* Original definition preserved */
Mixin Pattern Example
/* Mixin for logging functionality */
Loggable = class {
log = op(message) {
$time() + ": " + message;
};
logError = op(error) {
log("ERROR: " + error);
};
};
/* Mixin for validation functionality */
Validatable = class {
validate = op(data) {
if (data == null || data == "") {
false;
} else {
true;
};
};
validateAndLog = op(data) {
if (validate(data)) {
log("Validation passed: " + data);
true;
} else {
logError("Validation failed: " + data);
false;
};
};
};
/* Class using multiple mixins */
DataProcessor = class ($OBJ, Loggable, Validatable) {
process = op(data) {
if (validateAndLog(data)) {
"Processed: " + data;
} else {
"Failed to process: " + data;
};
};
};
/* Usage */
processor = DataProcessor();
processor.process("valid data"); /* Returns: "Processed: valid data" */
processor.process(""); /* Returns: "Failed to process: " */
Best Practices
Class Design
- Single Responsibility: Each class should have a clear, single purpose
- Composition over Inheritance: Prefer composition when inheritance isn't necessary
- Clear Naming: Use descriptive names that don't start with "$"
- Documentation: Document class purpose and method behavior
Inheritance Usage
- Inherit from
$OBJ
: For user classes that need universal method support - Avoid Deep Hierarchies: Keep inheritance chains shallow and clear
- Use Mixins: For cross-cutting concerns like logging, validation, etc.
- Override Appropriately: Only override methods when you need different behavior
Variable Management
- Initialize in Class: Set default values in class definition
- Modify in Instances: Let copy-on-write handle instance-specific values
- Use Methods: Access variables through methods for encapsulation
- Avoid Direct Access: Prefer method access over direct variable access
Performance Considerations
- Shared Variables: Leverage copy-on-write for memory efficiency
- Method Inheritance: Reuse method definitions across instances
- System Classes: Use system classes for specialized functionality
- User Classes: Use user classes for application-specific logic
Notes
- Runtime Modification: Classes can be modified after definition
- Dynamic Typing: All type checking happens at runtime
- Memory Efficiency: Copy-on-write minimizes memory usage
- Method Resolution: Automatic method lookup through inheritance chain
- System Integration: Seamless integration with Grapa's type system