///|
/// A named, tracked input cell — a thin wrapper around Signal[T].
///
/// TrackedCell provides the same functionality as Signal but is intended for
/// use cases where you want a more structured, named input cell. All operations
/// delegate directly to the inner Signal.
///
/// # Example
///
/// ```moonbit nocheck
/// let rt = Runtime()
/// let cell = TrackedCell(rt, 0, label="counter")
/// cell.set(5)
/// inspect(cell.get(), content="5")
/// ```
pub(all) struct TrackedCell[T] {
priv signal : Signal[T]
} derive(Debug(ignore=[Signal]))
///|
/// Constructor-syntax alias of `TrackedCell::new`. Enables
/// `TrackedCell(rt, value)` / `TrackedCell(rt, value, label="...")` call sites.
pub fn[T] TrackedCell::TrackedCell(
rt : Runtime,
initial : T,
durability? : Durability = Low,
label? : String,
) -> TrackedCell[T] {
TrackedCell::new(rt, initial, durability~, label?)
}
///|
/// Creates a new TrackedCell with the given initial value.
///
/// # Parameters
///
/// - `rt`: The runtime that will manage this cell
/// - `initial`: The initial value of the cell
/// - `durability`: How often this cell is expected to change (default: `Low`)
/// - `label`: An optional human-readable name for debugging and cycle error output
///
/// # Returns
///
/// A new TrackedCell containing the initial value
///
/// # Example
///
/// ```moonbit nocheck
/// let cell = TrackedCell(rt, 42)
///
/// let config = TrackedCell(rt, "prod", durability=High)
///
/// let named = TrackedCell(rt, 0, label="counter")
/// ```
pub fn[T] TrackedCell::new(
rt : Runtime,
initial : T,
durability? : Durability = Low,
label? : String,
) -> TrackedCell[T] {
{ signal: Signal::new(rt, initial, durability~, label?) }
}
///|
/// Returns the current value of the cell.
///
/// If called inside a memo's compute function, this automatically records
/// a dependency from the memo to this cell. When the cell changes,
/// the memo will know to reverify.
///
/// # Returns
///
/// The current value of the cell
pub fn[T] TrackedCell::get(self : TrackedCell[T]) -> T {
self.signal.get()
}
///|
/// Returns the current value of the cell without recording a dependency.
///
/// Delegates to `Signal::peek()` on the inner signal. Use from outside
/// the dependency graph when you don't want to trigger recomputation.
pub fn[T] TrackedCell::peek(self : TrackedCell[T]) -> T {
self.signal.peek()
}
///|
/// Returns the current value of the cell as a Result.
///
/// This method exists for API consistency with `Memo::get_result()`.
/// Since TrackedCells cannot have cycles (they are input cells with no
/// dependencies), this method always returns `Ok(value)`.
///
/// # Returns
///
/// `Ok(value)` with the current value of the cell
pub fn[T] TrackedCell::get_result(
self : TrackedCell[T],
) -> Result[T, CycleError] {
self.signal.get_result()
}
///|
/// Sets the cell to a new value.
///
/// If the new value equals the current value (via `Eq`), this is a no-op:
/// no revision bump occurs, and downstream memos won't reverify.
///
/// During a batch (`Runtime::batch`), the write is deferred. At batch end,
/// only cells whose final value differs from the pre-batch value trigger
/// a revision bump.
///
/// # Parameters
///
/// - `value`: The new value to set
pub fn[T : Eq] TrackedCell::set(self : TrackedCell[T], value : T) -> Unit {
self.signal.set(value)
}
///|
/// Sets the cell to a new value, always bumping the revision.
///
/// Unlike `set`, this does not check for equality. Use this when you want
/// to force downstream memos to reverify even if the value is the same,
/// or when your type doesn't implement `Eq`.
///
/// # Parameters
///
/// - `value`: The new value to set
pub fn[T] TrackedCell::set_unconditional(
self : TrackedCell[T],
value : T,
) -> Unit {
self.signal.set_unconditional(value)
}
///|
/// Returns the unique identifier for this cell.
///
/// The CellId can be used with `Runtime::cell_info()` to retrieve
/// metadata, or to compare cell identities.
///
/// # Returns
///
/// The cell identifier for this cell
pub fn[T] TrackedCell::id(self : TrackedCell[T]) -> CellId {
self.signal.id()
}
///|
/// Returns the durability level of this cell.
///
/// Durability indicates how often this cell is expected to change:
/// - `High`: Rarely changes (e.g., configuration)
/// - `Medium`: Moderately stable
/// - `Low`: Frequently changes (e.g., user input)
///
/// # Returns
///
/// The durability level set at construction time
pub fn[T] TrackedCell::durability(self : TrackedCell[T]) -> Durability {
self.signal.durability()
}
///|
/// Registers a callback that fires whenever this cell's value changes.
///
/// The callback receives the new value. Only one callback can be registered
/// at a time; calling this again replaces the previous callback.
///
/// # Parameters
///
/// - `f`: Called with the new value whenever this cell changes
pub fn[T] TrackedCell::on_change(
self : TrackedCell[T],
f : (T) -> Unit,
) -> Unit {
self.signal.on_change(f)
}
///|
/// Removes the `on_change` callback for this cell.
pub fn[T] TrackedCell::clear_on_change(self : TrackedCell[T]) -> Unit {
self.signal.clear_on_change()
}
///|
/// Returns true. TrackedCells are always up-to-date since they are input
/// cells with directly-set values.
pub fn[T] TrackedCell::is_up_to_date(self : TrackedCell[T]) -> Bool {
self.signal.is_up_to_date()
}
///|
/// Disposes this tracked cell by disposing its inner signal.
pub fn[T] TrackedCell::dispose(self : TrackedCell[T]) -> Unit {
self.signal.dispose()
}
///|
/// Returns true if this tracked cell has been disposed.
pub fn[T] TrackedCell::is_disposed(self : TrackedCell[T]) -> Bool {
self.signal.is_disposed()
}
///|
/// Returns the inner Signal for interop with APIs that expect a Signal[T].
///
/// # Returns
///
/// The underlying Signal[T] wrapped by this TrackedCell
pub fn[T] TrackedCell::as_signal(self : TrackedCell[T]) -> Signal[T] {
self.signal
}