So far we've used the types steav2 gives us: numbers, text, Booleans and arrays. But what if you're making a game with pets? A pet has a name and an age and a favourite food. You could keep three separate variables for each pet, but that gets messy fast. Instead, you can invent a brand new type called Pet that bundles them all together.
What is a sampoan?
sampoan means struct (short for "structure"). A struct is a type you design yourself, made up of named pieces called fields:
bongSlanhOun "pets";
sampoan Pet {
akthe name: Ahsor;
akthe age: LekKut;
}
akthe myPet = Pet("Mew", 3);
jongyeytha myPet;
jongyeytha myPet.name;
jongyeytha myPet.age; Pet(Mew, 3)
Mew
3 Let's unpack that:
sampoan Pet { ... }is the blueprint. It says "aPethas aname, which is text, and anage, which is a whole number". Each field is written like a variable with its type, but without a value.Pet("Mew", 3)builds a pet from the blueprint. You list a value for each field, in the same order as in the blueprint: first the name, then the age.myPet.namereads one field, using a dot. Read it as "myPet's name".
By tradition, type names start with a capital letter (Pet, Ahsor, LekKut), and variables start with a small one (myPet).
Changing fields
You can change a field just like a variable:
bongSlanhOun "birthday";
sampoan Pet {
akthe name: Ahsor;
akthe age: LekKut;
}
akthe myPet = Pet("Mew", 3);
myPet.age += 1;
myPet.name = "Mew the Great";
jongyeytha myPet; Pet(Mew the Great, 4) Methods
A struct can have its own functions too, written inside the blueprint. These are called methods (you've already used some: .len() and .push(x) on arrays). Inside a method, you can use the fields by name:
bongSlanhOun "methods";
sampoan Rectangle {
akthe width: Lek;
akthe height: Lek;
rupamun area() -> Lek {
morvenh width * height;
}
rupamun isSquare() -> Boolean {
morvenh width == height;
}
}
akthe door = Rectangle(1, 2);
akthe tile = Rectangle(3, 3);
jongyeytha "door area: " + door.area();
jongyeytha "tile area: " + tile.area();
jongyeytha tile.isSquare(); door area: 2
tile area: 9
ok When you call door.area(), width and height mean door's width and height. Call tile.area() and they mean tile's.
Structs are copied
Here's something important about structs. When you put a struct into another variable, you get a copy, a completely separate twin:
bongSlanhOun "copies";
sampoan Point {
akthe x: Lek;
akthe y: Lek;
}
akthe a = Point(1, 2);
akthe b = a;
b.x = 100;
jongyeytha a;
jongyeytha b; Point(1, 2)
Point(100, 2) Changing b didn't touch a. It's like photocopying a drawing and then scribbling on the copy: the original stays neat. The same happens when you pass a struct into a function: the function gets its own copy to play with.
Because a method also gets a copy, a struct's method isn't allowed to change the struct's fields. steav2 stops you, rather than letting the change silently vanish:
bongSlanhOun "no changes";
sampoan Pet {
akthe name: Ahsor;
akthe age: LekKut;
rupamun birthday() {
age += 1;
}
} [line 7] Error at 'age': a sampoan method gets a copy of nis, it can't change 'age' (mutate the struct from outside instead) (nis means this: the value a method was called on. You'll see a lot more of it in the next lesson.) The struct way to do it is to have the method return a new, changed copy, and store that:
bongSlanhOun "older";
sampoan Pet {
akthe name: Ahsor;
akthe age: LekKut;
rupamun older() -> Pet {
morvenh Pet(name, age + 1);
}
}
akthe myPet = Pet("Mew", 3);
myPet = myPet.older();
jongyeytha myPet; Pet(Mew, 4) Arrays of structs
Structs and arrays work great together. The type of a list of pets is [Pet]:
bongSlanhOun "shelter";
sampoan Pet {
akthe name: Ahsor;
akthe age: LekKut;
}
akthe shelter: [Pet] = [Pet("Mew", 3), Pet("Rex", 5)];
shelter.push(Pet("Bubbles", 1));
somhab (akthe i: LekKut = 0; i < shelter.len(); i += 1) {
jongyeytha shelter[i].name + " is " + shelter[i].age;
} Mew is 3
Rex is 5
Bubbles is 1 Bonus: teaching + new tricks
What does + mean for two points? steav2 doesn't know until you tell it! You can write a function whose name is an operator, and steav2 will use it whenever that operator appears between your types. This is called operator overloading:
bongSlanhOun "operators";
sampoan Point {
akthe x: Lek;
akthe y: Lek;
}
rupamun +(a: Point, b: Point) -> Point {
morvenh Point(a.x + b.x, a.y + b.y);
}
rupamun ==(a: Point, b: Point) -> Boolean {
morvenh a.x == b.x ng a.y == b.y;
}
akthe start = Point(1, 2);
akthe step = Point(10, 0);
akthe finish = start + step;
jongyeytha finish;
jongyeytha finish == Point(11, 2); Point(11, 2)
ok Without the == function, finish == Point(11, 2) wouldn't work at all: steav2 never guesses what "equal" should mean for your own types.
Your turn
- Make a
sampoan Bookwith atitle, anauthorand a number ofpages, build two books, and print them. Hint:Book("...", "...", 300)in field order. - Add a method
isLong() -> BooleantoBookthat says whether it has more than 300 pages. - Make a
sampoan Moneywith one fieldcents: LekKut, and a+for it, soMoney(250) + Money(99)printsMoney(349).
Solution for exercise 3:
bongSlanhOun "money";
sampoan Money {
akthe cents: LekKut;
}
rupamun +(a: Money, b: Money) -> Money {
morvenh Money(a.cents + b.cents);
}
jongyeytha Money(250) + Money(99); Money(349)