Operations

An Operation is a marker struct which is used to specify a way of transforming a Derived Node with a set of dependencies.

In Depends, you define an Operation with #[derive(Operation)]. For these types, you can now implement UpdateDerived to specify how to transform data in to the target.

Here is an example of an Operation that squares a number:

#[derive(Value, Hash)]
pub struct SomeNumber {
    pub value: i32,
}

#[derive(Operation)]
pub struct Square;

impl UpdateDerived<DepRef<'_, SomeNumber>, Square> for SomeNumber {
    fn update(&mut self, deps: DepRef<'_, SomeNumber>) -> Result<(), EarlyExit> {
        self.value = deps.value.pow(2);
        Ok(())
    }
}

Above, we're expressing that given a DepRef (single dependency read-reference) to a node holding SomeNumber, we can pass the Square operation to transform the value of a Derived Node holding SomeNumber.

The update method describes how to use the dependencies (the input) to update the internal value of the derived node.

This operation will take a number and square it. In practice, operations can be any function that transforms the inputs into a new state for the target.

Early Exit

For some graphs, it may be desirable to exit early from an operation. This can be achieved by returning Err(EarlyExit) from the update_derived method.

#[derive(Operation)]
pub struct CheckAllIsOk;

impl UpdateDerived<DepRef<'_, SomeNumber>, CheckAllIsOk> for SomeNumber {
    fn update(&mut self, deps: DepRef<'_, SomeNumber>) -> Result<(), EarlyExit> {
        if deps.value >= 100 {
            return Err(EarlyExit::new("Things are a bit too spicy!"));
        }
        self.value = deps.value;
        Ok(())
    }
}

This is particularly useful if you want to short-circuit a costly computation when it's clear that the result is no longer relevant.

Early exit will be triggered by the first value which returns an Err, therefore ordering is important.

Be aware that nodes after the node which prompts the exit will not receive data during the execution, and will miss any transient state (that which will be cleaned) which is cleared up by the time they are next updated.