dotmod

Immutable modifications to nested data structures

Part of the Shape pillar, dotmod provides surgical, immutable modifications to nested data structures using dot notation paths.

Overview

dotmod enables you to modify values deep within nested structures without mutating the original data, following functional programming principles.

Core Operations

set_ - Set a value at a path

from shape.dotmod import set_

data = {
    "user": {
        "name": "Alice",
        "role": "user"
    }
}

# Create a new structure with modified value
new_data = set_(data, "user.role", "admin")

# Original unchanged
print(data["user"]["role"])      # "user"
print(new_data["user"]["role"])  # "admin"

delete - Remove a path

from shape.dotmod import delete

data = {
    "user": {
        "name": "Alice",
        "email": "alice@example.com",
        "temp_token": "xyz123"
    }
}

# Remove temporary token
clean_data = delete(data, "user.temp_token")
# {"user": {"name": "Alice", "email": "alice@example.com"}}

update - Apply a function to a value

from shape.dotmod import update

data = {
    "product": {
        "name": "Widget",
        "price": 10.00
    }
}

# Apply discount
discounted = update(data, "product.price", lambda p: p * 0.9)
# {"product": {"name": "Widget", "price": 9.00}}

Creating Nested Structures

dotmod automatically creates intermediate structures as needed:

from shape.dotmod import set_

data = {}

# Creates nested structure
result = set_(data, "user.profile.settings.theme", "dark")
# {
#     "user": {
#         "profile": {
#             "settings": {
#                 "theme": "dark"
#             }
#         }
#     }
# }

Working with Lists

Modify list elements by index:

data = {
    "items": [
        {"id": 1, "status": "pending"},
        {"id": 2, "status": "pending"},
        {"id": 3, "status": "pending"}
    ]
}

# Update second item's status
result = set_(data, "items.1.status", "completed")

# Extend lists automatically
result = set_(data, "items.3.status", "new")
# Automatically extends the list

Immutability Guarantees

All operations return new structures with shared, unchanged parts:

original = {
    "a": {"x": 1, "y": 2},
    "b": {"x": 3, "y": 4}
}

# Modify a.x
modified = set_(original, "a.x", 10)

# Only 'a' is cloned, 'b' is shared
assert original["b"] is modified["b"]  # True (shared)
assert original["a"] is not modified["a"]  # True (cloned)

Real-World Examples

Configuration Updates

config = {
    "server": {
        "host": "localhost",
        "port": 8080,
        "ssl": False
    }
}

# Update for production
prod_config = (
    set_(config, "server.host", "api.example.com")
    |> set_("server.port", 443)
    |> set_("server.ssl", True)
)

State Management

app_state = {
    "user": {"id": 1, "name": "Alice"},
    "ui": {"theme": "light", "sidebar": True}
}

# User action: toggle theme
new_state = update(app_state, "ui.theme", 
                  lambda t: "dark" if t == "light" else "light")

Data Sanitization

user_data = {
    "profile": {
        "name": "Alice",
        "email": "alice@example.com",
        "password": "secret123",  # Don't send to client!
        "ssn": "123-45-6789"      # Sensitive!
    }
}

# Remove sensitive fields
public_data = (
    delete(user_data, "profile.password")
    |> delete("profile.ssn")
)

Integration with Other Tools

from depth.dotget import get
from shape.dotmod import set_
from truth.dotexists import check

def safe_increment(data, path, amount=1):
    """Safely increment a numeric value."""
    if check(data, path):
        current = get(data, path) or 0
        return set_(data, path, current + amount)
    return set_(data, path, amount)

Mathematical Foundation

dotmod implements a functional update operation that preserves referential transparency: - Immutability: f(data) produces new data, original unchanged - Path-based addressing: Updates are localized to paths - Structural sharing: Unchanged parts share memory