Updatable Protocol
Immutable update operations for data structures
The Updatable protocol defines the ability to update immutable data structures in a type-safe way, returning new instances with modified values. The DeepUpdatable protocol extends this for nested updates.
An Updatable type supports updating fields in immutable data structures, ensuring you never mutate the original data.
Understanding Updatable
The Updatable protocol represents types that can be updated immutably. Instead of modifying data in place, update operations return new instances with the changes applied.
from dataclasses import dataclass
@dataclass
class User:
name: str
age: int
user = User("Alice", 30)
# Instead of: user.age = 31 # Mutates!
updated = user.update(age=31) # Returns new instanceUse Updatable when you need to modify immutable data structures like configuration objects or state.
Updatable Methods
Set
The set method updates a single field.
result.set("name", "Bob")Returns a new instance with the field updated.
Update
The update method updates multiple fields at once.
result.update(name="Bob", age=25)Returns a new instance with all specified fields updated.
Delete
The delete method removes a field.
result.delete("temporary_field")Returns a new instance with the field removed.
Merge
The merge method combines another structure into this one.
result.merge({"extra": "value"})Returns a new instance with merged data.
DeepUpdatable: Nested Updates
The DeepUpdatable protocol supports updating nested fields using paths.
Set In
The set_in method sets a nested field using a path.
data.set_in(["user", "preferences", "theme"], "dark")Returns a new instance with the nested field updated.
Update In
The update_in method updates multiple fields at a nested level.
data.update_in(["user", "preferences"], theme="dark", notifications=True)Returns a new instance with nested fields updated.
Delete In
The delete_in method deletes a field at a nested level.
data.delete_in(["user", "cache"], "temporary_data")Returns a new instance with the nested field removed.
Implementing Updatable
To implement Updatable for your own types, define update methods that return new instances.
from dataclasses import replace
@dataclass
class Config:
host: str
port: int
debug: bool
def set(self, key, value):
return replace(self, **{key: value})
def update(self, **changes):
return replace(self, **changes)Use Python's dataclasses.replace() for easy immutable updates.
Real-World Pattern: Immutable Configuration
from dataclasses import dataclass, replace
@dataclass
class DatabaseConfig:
host: str
port: int
username: str
password: str
# Base configuration
base_config = DatabaseConfig(
host="localhost",
port=5432,
username="user",
password="pass"
)
# Environment-specific configurations
dev_config = base_config.update(
host="dev.local",
password="dev_pass"
)
prod_config = base_config.update(
host="prod.example.com",
password=prod_secure_password()
)
# Original is unchanged
base_config # Still has host="localhost"This pattern shows Updatable's strength: create base configurations and derive environment-specific versions without mutating the original.
Real-World Pattern: Nested State Updates
from dataclasses import dataclass, replace
@dataclass
class AppState:
user: dict
settings: dict
state = AppState(
user={"name": "Alice", "preferences": {"theme": "light"}},
settings={"version": "1.0"}
)
# Update nested preferences
updated_state = state.set_in(
["user", "preferences", "theme"],
"dark"
)
# Update multiple nested fields
updated_state = state.update_in(
["user", "preferences"],
theme="dark",
notifications=True
)
# Delete nested field
updated_state = state.delete_in(
["user", "cache"],
"temporary_data"
)This pattern shows DeepUpdatable's strength: update deeply nested data without mutation or complex restructuring.
When to Use
Use Updatable when:
- You need immutable data structures
- You want to track changes over time
- You're building state management systems
- You need safe data sharing
Use DeepUpdatable when:
- You have nested data structures
- You need to update deeply nested fields
- You want to avoid complex data restructuring
Don't use Updatable when:
- You need mutable data (use regular Python objects)
- Performance is critical (mutation is faster)
- You're working with simple values
See Also
- PersistentMap - Immutable map implementation
- PersistentList - Immutable list implementation
- State - Monad for state threading