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