tincan 0.3.0

A lightweight reactive state management library for Rust.
Documentation
//! Derived state and memoization example
//!
//! Demonstrates computed values, caching, and dependency tracking.
//!
//! Run with: cargo run --example derived_state

use std::cell::Cell;
use std::rc::Rc;
use tincan::Scope;

fn main() {
    println!("=== Derived State Example ===\n");

    let cx = Scope::new();

    // Source data
    let first_name = cx.signal(String::from("Alice"));
    let last_name = cx.signal(String::from("Smith"));
    let age = cx.signal(30);

    // Derived: full name
    let full_name = cx.memo({
        let first = first_name.clone();
        let last = last_name.clone();
        move || format!("{} {}", first.get(), last.get())
    });

    // Derived: greeting
    let greeting = cx.memo({
        let name = full_name.clone();
        let age = age.clone();
        move || format!("Hello, {}! You are {} years old.", name.get(), age.get())
    });

    // Display effect
    let greeting_clone = greeting.clone();
    let _display = cx.effect(move || {
        println!("👋 {}", greeting_clone.get());
    });

    println!("Updating first name...");
    first_name.set(String::from("Bob"));

    println!("\nUpdating age...");
    age.set(35);

    println!("\nUpdating last name...");
    last_name.set(String::from("Johnson"));

    // Demonstrating memoization efficiency
    println!("\n=== Memoization Efficiency ===\n");

    let cx2 = Scope::new();
    let input = cx2.signal(5);
    let computation_count = Rc::new(Cell::new(0));

    let expensive_computation = cx2.memo({
        let input = input.clone();
        let computation_count = Rc::clone(&computation_count);
        move || {
            computation_count.set(computation_count.get() + 1);
            println!("🔄 Computing... (count: {})", computation_count.get());
            // Simulate expensive operation
            input.get() * input.get()
        }
    });

    println!("First access:");
    println!("Result: {}", expensive_computation.get());

    println!("\nSecond access (should use cache):");
    println!("Result: {}", expensive_computation.get());

    println!("\nThird access (should still use cache):");
    println!("Result: {}", expensive_computation.get());

    println!("\nChanging input:");
    input.set(10);

    println!("Accessing after change (should recompute):");
    println!("Result: {}", expensive_computation.get());

    println!("\nAccessing again (should use cache):");
    println!("Result: {}", expensive_computation.get());

    println!("\nTotal computations: {}", computation_count.get());

    // Diamond dependency pattern
    println!("\n=== Diamond Dependency ===\n");

    let cx3 = Scope::new();
    let root = cx3.signal(10);

    let double = cx3.memo({
        let root = root.clone();
        move || {
            let val = root.get() * 2;
            println!("  Computing double: {}", val);
            val
        }
    });

    let triple = cx3.memo({
        let root = root.clone();
        move || {
            let val = root.get() * 3;
            println!("  Computing triple: {}", val);
            val
        }
    });

    let sum = cx3.memo({
        let double = double.clone();
        let triple = triple.clone();
        move || {
            let val = double.get() + triple.get();
            println!("  Computing sum: {}", val);
            val
        }
    });

    println!("Initial computation:");
    println!("Sum: {}\n", sum.get());

    println!("Accessing again (should use all caches):");
    println!("Sum: {}\n", sum.get());

    println!("Changing root value:");
    root.set(20);
    println!("Sum: {}", sum.get());
}