Skip to content

API Reference

This is the public API reference for FynX, a reactive programming library. Values are observables; when one changes, everything computed from it recalculates on its own, the way a spreadsheet cell updates every formula that references it.

Imperative vs. Declarative

Traditional (imperative): you write the code that updates the UI, recalculates a value, and writes it back, every time a variable changes, and you're responsible for remembering every place that needs updating.

FynX (declarative): you describe the relationship between values once. Change a value, and everything that depends on it updates in the right order.

Your Learning Path

FynX's API is designed to be learned progressively, with each concept building on the previous one:

  1. Start with Observables — Learn to create reactive values that notify subscribers when they change
  2. Organize with Stores — Group related observables into cohesive units with clean APIs
  3. Filter Conditionally — Control when reactions occur based on runtime conditions
  4. Automate with Decorators — Eliminate boilerplate and express reactive relationships declaratively

Each section in this reference assumes you understand the previous sections. If you encounter an unfamiliar concept, backtrack to the earlier pages.

Core Concepts

Observables: Reactive Values

Observables are containers for values that change over time. Unlike regular variables, they automatically notify anyone who's interested when their values change.

Observable — The foundation of FynX. Create observables with observable(initial_value), read them with .value, write them with .set(new_value). Every other FynX feature builds on this simple primitive.

ComputedObservable — Values that automatically recalculate when their explicit sources change. Create them with the >> operator: full_name = (first + last) >> (lambda f, l: f"{f} {l}"). The >> operator transforms observable values through pure functions. Alternatively, use the .then(func) method on observables for the same result. Transform functions may only use their arguments; combine additional observables first with + or .alongside().

MergedObservable — Combine multiple observables into a single reactive tuple using the + operator: position = x + y + z. When any source changes, subscribers receive all values as a tuple. This is the foundation for reactive relationships that depend on multiple values.

ConditionalObservable — Observables that emit when conditions are satisfied. Create them with the @ operator or .requiring(): valid_submission = form_data @ is_valid. This enables sophisticated reactive logic without cluttering your code with conditional checks.

Observable Descriptors — The mechanism behind Store class attributes. When you write name = observable("Alice") in a Store class, you're creating a descriptor that provides clean property access without .value or .set().

Observable Operators — The operators (>>, +, &, |, ~, @) and methods (.then(), .alongside(), .all(), .either(), .negate(), .requiring()) that let you compose observables into reactive pipelines. >> transforms observables through pure functions; the rest make multiple inputs and conditions explicit.

Stores: Organizing State

While standalone observables are useful for small scripts, real applications need structure. Stores group related observables and computed values into cohesive units.

Store & @observable — Create Store classes that encapsulate related state. Use @observable to make class attributes reactive, or use observable() as a class attribute descriptor. This gives you clean property access: UserStore.name = "Alice" instead of user_name.set("Alice").

Decorators: Declarative Reactions

Decorators let you declare what should happen when observables change, without manually managing subscriptions.

@reactive — Run functions automatically when dependencies change: logging, UI updates, API calls, anything that should happen in response to state changes. The function runs immediately and again whenever any observable it reads changes. Use with conditional observables for event-driven reactions: @reactive(payload @ ready).

API Quick Reference

Creating Reactive State

from fynx import observable, Store

# Standalone observables
count = observable(0)
name = observable("Alice")

# Store-based observables
class AppStore(Store):
    count = observable(0)
    name = observable("Alice")

Reading and Writing

# Standalone observables
current = count.value          # Read
count.set(current + 1)         # Write

# Store observables
current = AppStore.count       # Read
AppStore.count = current + 1   # Write

Deriving Values

# Using the >> operator (recommended)
doubled = count >> (lambda c: c * 2)
full_name = (first + last) >> (lambda f, l: f"{f} {l}")

# Using .then() method (alternative syntax)
doubled = count.then(lambda c: c * 2)
full_name = (first + last).then(lambda f, l: f"{f} {l}")

Reacting to Changes

# Manual subscription
count.subscribe(lambda val: print(f"Count: {val}"))

# Using @reactive decorator
@reactive(count)
def log_count(val):
    print(f"Count: {val}")

# Using @reactive with conditional observables for event-driven reactions
is_above_threshold = count >> (lambda c: c > 10)
@reactive(is_above_threshold)
def on_threshold(is_above):
    if is_above:
        print("Count exceeded 10!")

Composing Observables

# Merge multiple sources
position = x + y + z

# Transform values with >> operator
doubled = count >> (lambda c: c * 2)

# Or use .then() method
doubled = count.then(lambda c: c * 2)

# Build boolean conditions
should_save = has_changes & is_valid

# Gate values conditionally
valid_payload = payload @ should_save

# Negate conditions
is_idle = ~is_busy

Complete Example: Putting It All Together

Here's how these concepts work together in a realistic scenario:

from fynx import Store, observable, reactive

class ShoppingCartStore(Store):
    # Basic reactive state
    items = observable([])
    discount_code = observable(None)

# Computed values using >> operator
subtotal = ShoppingCartStore.items >> (
    lambda items: sum(item['price'] * item['quantity'] for item in items)
)

discount_amount = (ShoppingCartStore.items + ShoppingCartStore.discount_code) >> (
    lambda items, code: sum(item['price'] * item['quantity'] for item in items) * 0.20
    if code == "SAVE20" else 0.0
)

total = (subtotal + discount_amount) >> (
    lambda sub, disc: sub - disc
)

# Boolean observable for checkout eligibility
has_items = ShoppingCartStore.items >> (lambda i: len(i) > 0)
total_positive = total >> (lambda t: t > 0)
can_checkout = has_items & total_positive

# React to changes automatically
# @reactive fires immediately with the current value, then again on every change
@reactive(total)
def update_ui_total(t):
    print(f"💰 New total: ${t:.2f}")
# Output immediately: 💰 New total: $0.00

# React to checkout eligibility using conditional observables
@reactive(can_checkout)
def enable_checkout_button(can_checkout_val):
    if can_checkout_val:
        print("✅ Checkout button enabled")

# Use the store
ShoppingCartStore.items = [
    {'name': 'Widget', 'price': 10.00, 'quantity': 2}
]
# Output: 💰 New total: $20.00
# Output: ✅ Checkout button enabled

ShoppingCartStore.discount_code = "SAVE20"
# Output: 💰 New total: $16.00

Documentation Conventions

Throughout this reference, we follow consistent patterns:

  • Type signatures use Python type hints for clarity and enable IDE autocomplete
  • Examples progress from simple to complex within each page
  • Notes highlight gotchas that trip up newcomers
  • Performance tips appear when relevant to optimization decisions
  • See also links connect related concepts and alternative approaches

New to FynX?

Read in order: ObservableStore@reactiveConditionalObservable

Building an application?

Focus on: Store, Observable Operators (especially >>), @reactive

Need complex state logic?

See: Observable Operators, ConditionalObservable, @reactive

Performance optimization?

See: ComputedObservable for memoization, Observable for subscription management

Curious about implementation?

Explore: Observable Descriptors to understand how the magic works


For conceptual introductions and tutorials, return to the main documentation.