State encapsulation: Each instance has independent state
Implicit self: Like Java’s this, no need to pass self
Role Init: Constructor-like initialization
Actions and functions: Can have both
Role Init Action
The Init action in a role acts like a constructor:
roleAccount:actionInit:# Initialize instance stateself.balance=0self.owner=None# Create instance with parametersactionInit:account=Account(INITIAL_BALANCE=100,ACCOUNT_ID=1)
State Variables
Only self.* variables are part of role state:
roleExample:actionInit:self.persisted=0# Part of statetemp=100# NOT part of state (local variable)
Special Role Fields
self.__id__ - Auto-generated unique identifier:
roleServer:actionInit:self.name="server"# self.__id__ is automatically set by FizzBeeactionLogStatus:print(f"Server {self.__id__}: {self.status}")actionInit:servers=[]foriinrange(3):servers.append(Server())# servers[0].__id__ != servers[1].__id__ != servers[2].__id__
Use cases:
Distinguishing role instances in logs
Using instance ID in message routing
Debugging and visualization
Note: __id__ is read-only and unique across all role instances
Durable vs Ephemeral State
Use @state decorator to mark ephemeral fields:
@state(ephemeral=['cache'])roleServer:actionInit:self.disk_data=0# Durable (persists across crashes)self.cache=0# Ephemeral (lost on crash)
Alternatively, mark durable fields:
@state(durable=['disk_data'])roleServer:actionInit:self.disk_data=0# Durableself.cache=0# Ephemeral (not in durable list)
Rules:
Cannot specify both durable and ephemeral
Default: all state is durable
On crash: ephemeral fields reset to Init values
Role Communication
Roles communicate by calling each other’s functions:
# Example 0046: Simple Role# Basic role definition (like a class)# Demonstrates: role keyword, role structurerole Account:
action Init:
# self prefix makes it part of role state self.balance =0 self.owner ="alice" action Deposit:
require self.balance < 100 self.balance = self.balance + 10# Create role instanceaction Init:
account= Account()# Role instances can have their own state# Multiple instances would have independent stateatomic action NoOp:
if account.balance >= 100:
pass
# Example 0047: Role Init# Role Init action is like a constructor# Demonstrates: role initialization with parametersrole Account:
# Init is called when role is instantiated action Init:
# self.OWNER comes from constructor parameter self.balance =0 self.name = self.OWNER # Use parameter action Deposit:
require self.balance < 50 self.balance = self.balance + 10# Create role instances with parametersaction Init:
alice_account= Account(OWNER="alice")bob_account= Account(OWNER="bob")counter=0atomic action IncrementCounter:
require counter < 2counter= counter + 1# Each instance has its own state# alice_account.name = "alice"# bob_account.name = "bob"atomic action NoOp:
if counter >= 2:
pass
# Example 0048: Role Actions# Actions within roles# Demonstrates: role actions, implicit selfrole Account:
action Init:
self.balance =0# Role actions don't take parameters (like top-level actions)# self is implicit (no need to declare) action Deposit:
require self.balance < 50 self.balance = self.balance + 10 action Withdraw:
require self.balance >=10 self.balance = self.balance - 10action Init:
account= Account()# Role actions are called by the model checker# Just like top-level actions# The model checker explores all enabled role actionsatomic action NoOp:
pass
# Example 0050: Role State# Only self.* variables are part of role state# Demonstrates: role state variables vs local variablesrole Account:
action Init:
# State variables (persisted) self.balance =0 self.transactions =0# Local variable (not persisted)temp=100# This is NOT part of state action Deposit:
require self.balance < 50# Local variables don't affect stateamount=10# Only self.* updates are persisted self.balance = self.balance + amount
self.transactions = self.transactions + 1action Init:
account= Account()# Only self.balance and self.transactions are part of state# Local variables like temp, amount are not trackedatomic action NoOp:
if account.balance >= 50:
pass
# Example 0051: Role Decorators# @state decorator marks durable vs ephemeral fields# Demonstrates: @state(durable=[...]) and @state(ephemeral=[...])# Mark cache as ephemeral (lost on crash)@state(ephemeral=['cache'])role Storage:
action Init:
# Durable state (persists across crashes) self.data ={}# Ephemeral state (lost on crash) self.cache ={} atomic action Write:
require len(self.data) < 2key="key_" + str(len(self.data)) self.data[key]="value" self.cache[key]="cached_value"# Can also mark durable explicitly:# @state(durable=['data'])# This makes everything else ephemeralaction Init:
storage= Storage()atomic action NoOp:
if len(storage.data) >= 2:
pass