Actor Model Paradigm#

Initial Set-Up#

import pykka
from typing import Optional

Predecessor and Successor#

class Predecesser(pykka.ThreadingActor):
    def __init__(self, current_value: Optional[int] = None):
        super().__init__()
        self.current_value = current_value

    def on_receive(self, message: None = None) -> int:
        if self.current_value is None:
            raise ValueError("No current_value was set")

        self.current_value -= 1
        return self.current_value


assert Predecesser().start(current_value=1).ask(None) == 0
assert Predecesser().start(current_value=10).ask(None) == 9
class Successer(pykka.ThreadingActor):
    def __init__(self, current_value: Optional[int] = None):
        super().__init__()
        self.current_value = current_value

    def on_receive(self, message: None = None) -> int:
        if self.current_value is None:
            raise ValueError("No current_value was set")

        self.current_value += 1
        return self.current_value


assert Successer().start(current_value=0).ask(None) == 1
assert Successer().start(current_value=10).ask(None) == 11

Addition#

class Adder(pykka.ThreadingActor):
    def __init__(self, initial_value: Optional[int] = None):
        super().__init__()
        self.initial_value = initial_value
        self.current_value: Optional[int] = None

    def on_receive(self, message: int) -> int:
        if self.initial_value is None or message is None:
            raise ValueError("Both addends should be specified")

        if self.current_value is None:
            self.current_value = self.initial_value

        successer = Successer().start(current_value=self.current_value)

        for _ in range(message):
            successer.tell(None)

        successer_proxy = successer.proxy()
        result_future = successer_proxy.current_value
        result: int = result_future.get()
        self.current_value = result
        return self.current_value


assert Adder().start(initial_value=0).ask(0) == 0
assert Adder().start(initial_value=1).ask(0) == 1
assert Adder().start(initial_value=0).ask(1) == 1
assert Adder().start(initial_value=10).ask(10) == 20

Multiplication#

class Multiplier(pykka.ThreadingActor):
    def __init__(self, multiplicand: Optional[int] = None):
        super().__init__()
        self.multiplicand = multiplicand
        self.current_value = None

    def on_receive(self, message: int) -> int:
        if self.multiplicand is None or message is None:
            raise ValueError("Both multiplicand and multiplier should be specified")

        if self.multiplicand == 0 or message == 0:
            return 0

        if self.current_value is None:
            self.current_value = 0

        adder = Adder().start(initial_value=self.current_value)

        for _ in range(message):
            adder.tell(message)

        adder_proxy = adder.proxy()
        result_future = adder_proxy.current_value
        result: int = result_future.get()
        self.current_value = result
        return self.current_value


assert Multiplier.start(multiplicand=0).ask(0) == 0
assert Multiplier.start(multiplicand=2).ask(0) == 0
assert Multiplier.start(multiplicand=0).ask(2) == 0
assert Multiplier.start(multiplicand=10).ask(10) == 100

Exponentiation#

class Power(pykka.ThreadingActor):
    def __init__(self, base: Optional[int] = None):
        super().__init__()
        self.base = base
        self.current_value = None

    def on_receive(self, message: int) -> int:
        if self.base is None or message is None:
            raise ValueError("Both base and exponent should be specified")

        if self.base == 0:
            return 0

        if message == 0:
            return 1

        if self.current_value is None:
            self.current_value = 1

        multiplier = Multiplier().start(multiplicand=1)

        for _ in range(message):
            multiplier.tell(message)

        multiplier_proxy = multiplier.proxy()
        result_future = multiplier_proxy.current_value
        result: int = result_future.get()
        self.current_value = result
        return self.current_value


assert Power().start(base=1).ask(0) == 1
assert Power().start(base=0).ask(1) == 0
assert Power().start(base=3).ask(3) == 27