Gallery
Mochaccino
Share
Explore

icon picker
Structs

Introduction to Structs

Structs are a very fundamental concept in Mochaccino. They are used to define custom types and interfaces, and offer very fine-grained control over your data. So buckle up, because this is going to be a long section.

Constructor

What does the constructor in an OOP language do? It initialises class properties and performs an action when the class is first instantiated. Constructors in Mochaccino play a similar role, by taking care of type conversion.

Type Assertions

A struct can have a constructor, which is empty by default, that converts data to its own type. The constructor is called whenever a type conversion (aka type assertion) is taking place. A type conversion looks like this:
12::<string>
On the left is the value that needs to be converted and on the right is the conversion target type. In this case, the constructor for <string> is called with 12 as the argument.
Note
Implicit type assertions also take place during variable intialisation.

Constructor

So what does a constructor look like? It looks like a method defined for that struct:
struct string {
constructor(data<dynamic>)<string> {
return String.toString(data);
}
}
The constructor for this struct can take a value of any type and convert it to a string via the toString() method from the String module. But what’s with the return keyword? Well, the return keyword returns the elementary value of the struct. If you have no idea what an elementary value is, that’s okay, because that section comes . All you need to know is that if you log a class instance to the console, it will print the value returned by the constructor.
struct string {
constructor(data<dynamic>)<string> {
return String.toString(data);
}
}

struct xyz {
var someProp<int>;
constructor(data<dynamic>)<void> {
someProp = data;
}
}

var a<string> = 1::<string>;
var b<xyz> = 1::<xyz>;
Console.log(a); // "1"
Consoe.log(b); // null
Also, you may have noticed that the constructor can choose which types can be converted to their type.
struct MyJSON {
var someProp<int>;
constructor(data<map>)<void> {
someProp = data;
}
}

guarded Keyword

The guarded keyword in Mochaccino causes the succeeding statement to only be executed if it does not throw an error. If an except clause is specified, then the offending statement will be run, and the error will be handled by the except clause. This is typically used in type assertions, where one type is converted to another.
guarded 12::<Service> except pause; // program exits without errors
guarded 12::<Service>; // no type conversion takes place

Props

Props (short for properties) are used to define getters and setters in structs. But wait, what's the difference between properties and fields in a struct? A field in a struct is private by default, and not visible outside the struct. Hence, to make properties accessible and modifiable from outside the struct, you need to define properties. This is done using the prop keyword followed by the name of the property.
struct CustomNumber {
prop isEven {
get<bool> {
return ((this % 2)==0);
}
}
}
Here we see that we've created a custom struct for our own type of numbers. It has a property isEven that tells us whether the number is even or not. We can access that property because a getter is defined for it. In this case, it makes sense that a setter is not defined—you can't change whether a given number is even or not, as that depends on the number itself. Now let's look at a case where a setter needs to be defined.
struct customNumber {
var usedNumbers<array<customNumber>> = [];
prop hasBeenUsed {
get<bool> {
return Array.contains(userNumbers, this);
}
set (value<bool>)<void> {
if (value==true) {
} elif (value==false) {
}
}
}
}
However, props can only be used by certain structs, as you’ll find out in the next section.

Struct Annotation

Now would be a good time to introduce to you struct annotations, which are annotations preceded by @. These are used in defining structs and their behaviour, such as whether they can define props.

@elementary

These are three annotations you absolutely must know when using structs. One of these three special annotations must be provided when defining a struct. The @elementary keyword allows the struct to behave like a primitive type, where a struct instance has an elementary value. Of course, it’s easier to understand with an example:
struct JSON {
@elementary
}
var data<JSON> = someJsonData;
Console.print(data);
You see, the default value of data is the JSON data. You don’t have to create a field to store the actual data and then access it via dot notation. You could, if you wanted to, give it a custom elementary value as well. The constructor comes in handy, as the elementary value is the value returned by the constructor.
Elementary structs can only be inherited by other elementary structs, and can be used as type annotations.

this Keyword

The this keyword can be used within an elementary struct to access the elementary value.

@interface

In Mochaccino, structs can (and usually do) have abstract properties or methods. This is known as an interface, and is marked with the @interface annotation. Interfaces do not have an elementary value (because they define guidelines for more complex data). Interfaces can only be inherited by other interfaces, and can be used as type annotations.
struct JSON {
@interface
var value<map>;
func doStuff(number<int>)<void> async;
var value2<map>;
var value3<map>;
}
Notice how the properties in an interface do not need to be initialised. These are known as abstract members. A struct that inherits this struct has to provide concrete implementations for these members:
struct ApiJSON extends JSON {
var value<map> = {};
func doStuff(number<int>)<void> async {}
var value2<map> = {};
var value3<map> = {};
}
In some languages, when child classes override methods and properties, they are marked with @override, but not in Mochaccino. Mochaccino automatically knows, based on the name of the member, whether it is overriding the base class or not.

@protocol

But what if you wanted to define a guideline for modules? Struct-Module polymorphism is achieved through the @protocol annotation. Structs marked as protocols cannot be used as type annotations (obviously), and can only be inherited by other protocol structs or modules. Now, here’s the fun part. You can create members by typing in variables and methods as you would in a normal module, and you’re even allowed to define abstract implementations if needed.
struct ProtocolABC {
@protocol
var someVar = 10;
var anotherOne;
func someMethod()<void>;
func concrete()<void> {}
}
var data<JSON> = someJsonData;
Console.print(data);
You see, the default value of data is the JSON data. You don’t have to create a field to store the actual data and then access it via dot notation. You could, if you wanted to, give it a custom elementary value as well. The constructor comes in handy, as the elementary value is the value returned by the constructor.

Type Annotations

Type Arguments

A map is a data type which always has a left-hand side and right-hand side. In this case, we can specify types for each value to be more specific. We use a type annotation within a type annotation to do so:
var data<map<string, int>> = someMap;
Or you could do this if you had an array:
var data<array<string>> = someArray;
It gets even better when you have a map with an array of maps inside it:
var data<map<string, array<map<string, int>>>> = someMap;
Type annotations are wonderfully easy to read, aren’t they? Now, let’s try specifying a type for a list of maps that store a list of maps that store integers. Moving on to union types...

Union Types

Ever had a moment when you’re expecting a value from a function to be either of type X or Y? Union types to the rescue! A pipe symbol (|) can be used in a type annotation to specify a group of possible types.
var data<map|array> = jsonFromApi;

Struct Polymorphism

You’ve seen implements and extends being used for modules in the previous page. Same idea here, though there are more restrictions on how certain structs can be extended. For brevity’s sake, we’ll use the term inheriting to collectively refer to a child object implementing or extending a base struct.

Struct Inheritance Rules

Struct-Struct Inheritance

Elementary structs can only be inherited by elementary structs.
Abstract structs can only be inherited by abstract structs.
Unmarked structs (not marked with @elementary or @abstract) cannot be extended.

Struct-Module Inheritance

A module can only inherit from an abstract struct.
A module can have the same name as the struct it inherits from (as long as that name isn’t used by another module).

Implementing Structs

If you’re implementing a struct, you’re saying that the new struct conforms and inherits fully the properties and methods of the base struct. The inheriting object must provide concrete implementations of the

Extending Structs

If you’re extending a struct, you’re saying that the new struct

Application of Structs

Custom Types

struct apiV4JSON {
@elementary
constructor(data<map>)<map> {
return data;
}

prop version {
get<int> {
return this['ver'];
}
}

prop map {
get<map> {
return this;
}
}
}

Adding Functionality

Now that we’ve defined some properties, lets make

Props VS Methods

Sometimes, you might find that the distinction between a prop and a method gets blurred. In the string package from the core library, the String module contains a toLowerCase method. You might wonder why this isn’t made a property instead. A property must meet the following criteria:
Getters of the property must not take any arguments
Getters of the property must not modify the object itself in any way (except for its private fields).
The moment any of the above criteria isn’t met, you’ll need to create a module that provides the methods for the struct. Refer to the section of the for guidelines on the naming of structs and modules, especially when they complement each other.

Interfaces

Struct Polymorphism

Summary

Here’s a summary of the behaviour of various structs.
Struct Behaviours
Struct Type
Elementary Values
Props
Type Annotation
Inheritance
1
@elementary
@elementary
2
@interface
@interface
3
@protocol
@protocol, modules
There are no rows in this table
Share
 
Want to print your doc?
This is not the way.
Try clicking the ⋯ next to your doc name or using a keyboard shortcut (
CtrlP
) instead.