Objects - cyxigo/wi GitHub Wiki
Introduction
Wi is an object-oriented programming language. But unlike languages like Java or C#, Wi uses concatenative prototype-based OOP.
Instead of classes, Wi has objects. You create an object, and then you can clone it to make new objects. You can also add new fields to objects, extending them with more functionality.
Object definition
Objects, just like functions, are expressions. So to define an object you need to declare a variable and define it with the object expression:
o := object {
};
Objects are just bags of data. How do you define that data? You define fields:
o := object {
x: 0;
};
Now object o has a field called x with the value 0. To access that field, we use the field operator .:
o := object {
x: 0;
};
o.x = 1;
std::puts(o.x); %% 1
Methods
A method is a function associated with a specific object. To create a method, we simply define a function field:
o := object {
my_method: |self| => {
};
};
You may notice the parameter self – this is the object reference parameter. It's here so we can actually access the object's fields within its method.
Now, remember the invocation operator -> we talked about earlier? That's why it exists. If you access a method via the field operator . you need to pass self explicitly, but -> passes self implicitly!
o.my_method(o); %% bad (explicit self)
o->my_method(); %% good (implicit self)
It's not only easier to write ->, it's also faster in execution.
Cloning
We talked about how Wi relies on cloning, and we talked about the new operator earlier. Now it all comes together. But in case you forgot:
[!IMPORTANT] The new operator is used to clone an object. It creates a new object that is a copy of the original.
Here's an example:
obj_person := object {
name: "";
};
alice := new obj_person;
alice.name = "Alice";
bob := new obj_person;
bob.name = "Bob";
std::puts(alice.name); %% Alice
std::puts(bob.name); %% Bob
You may notice the obj_ prefix here. Wi has a convention that every object intended to be used as a template should start with obj_, to avoid confusion between the template and its clones.
But writing:
bob := new obj_person;
bob.name = "Bob";
Can become very much of a hustle if you got more than one field, that's why Wi supports this useful syntax:
bob := new obj_person {
name: "Bob";
};
You can even add new fields there:
bob := new obj_person {
name: "Bob";
job: "unemployed";
};
And of course, cloning works with methods too:
obj_person := object {
name: "";
greet: |self| => {
std::puts("Hello ${self.name}!");
};
};
alice := new obj_person {
name: "Alice";
};
alice->greet(); %% Hello Alice!
bob := new obj_person {
name: "Bob";
};
bob->greet(); %% Hello Bob!
Does that look familiar? Yeah, it's the example from the Wi repository README.md!
Merging
new can also be used to merge multiple objects into one clone! This is called merging in Wi. For example this:
obj_contact := object {
email: "";
phone: "";
};
obj_address := object {
street: "";
city: "";
};
obj_person := new obj_contact, obj_address {
name: "";
show_info: |self| => {
std::puts("${self.name} lives in ${self.city}, email: ${self.email}");
};
};
alice := new obj_person {
name: "Alice";
email: "[email protected]";
city: "New York";
};
alice->show_info(); %% Alice lives in New York, email: [email protected]
Quite a big example, but I don't have anything else, welp... To merge multiple objects, simply list them after new, separated by commas. If a name collision occurs between the merged objects, the rightmost object takes precedence:
obj_a := object {
x: 1;
};
obj_b := object {
x: 2;
};
c := new obj_a, obj_b;
std::puts(c.x); %% 2
Standard Method Library (STM)
You may have noticed that before we called methods on strings, arrays, and maps too! But how? They are not some undercover objects right?! Yeah of course they are not. It's the work of something called the Standard Method Library or STM in Wi. Let's say we had a hypothetical array library. Typing something like:
a := [1, 2, 3];
array.add(a, 4);
Would be horrible, especially in large programs. So, when you type:
a->add(4);
Somewhere, under the hood, Wi redirects this call to a method in STM!
But why STM is separated from STD (Standard Library)? Because they are different things, STD is a bunch of libraries and useful functions, STM is a bunch of methods for builtin types that you access only via -> on builtin types. If you want to read all about them check out Wi Standard Method Library Reference!
Next: Reference Types