Protocols

Protocols defines functionality in an abstract way that can be implemented by many classes.

Class and class inheritance is a good tool for structuring data and behavior. However, sometimes there is no clear parent / child relationship between classes, in which case using a protocol might be a more suitable choice.

Perhaps the simplest example is the Eq protocol, that defines equality between two objects, from the Acton builtins. Classes do not need to have any common inheritance in order to support equality.

protocol Eq:
    @staticmethod
    __eq__       : (Self,Self) -> bool

The __eq__ method takes two arguments, itself and the other object to compare with and returns True or False. Both arguments are of the same type Self, which means we can only compare two objects that are of the same type. It can be any type, as long as its the same type.

extension Circle (Eq):
    def __eq__(self, other):
        return self.radius == other.radius

Protocols allows us to express the requirement for a (generic) type to implement some particular functionality.

Let's say we have a function that compares two objects.

def comparator(a, b):
    print("Things:", a, b)
    print("Are they equal?", a == b)

We know that a and b must implement the Eq protocol and they must be of the same type. Let's see what the compiler constraint solver says:

acton --sigs generic.act


== sigs: generic ================================

comparator : [A(Eq)] => (a: A, b: A) -> None
=================================================

The type has been inferred to a generic type A that must implement the Eq protocol, which is written as A(Eq). We can write this explicitly:

def comparator[A(Eq)](a: A, b: A):
    print("Things:", a, b)
    print("Are they equal?", a == b)