Consistency problems

As a final warning we must analyze what happens when a field depends on another field multiple times, either directly or indirectly.

Take this example:

import com.femastudios.dataflow.extensions.* import com.femastudios.dataflow.listen.* import com.femastudios.dataflow.util.* fun main() { val num = mutableFieldOf(10) val isEven = num % 2 eq 0 val isOdd = num % 2 eq 1 val evenOrOdd = isEven or isOdd lifecycle { listen(num) { println("num = $it") } listen(evenOrOdd) { println("evenOrOdd = $it") } println() num.value = 20 num.value = 41 num.value = 56 } }
MutableField<Integer> num = MutableField.of(10); Field<Boolean> isEven = num.transform(n -> n % 2 == 0); Field<Boolean> isOdd = num.transform(n -> n % 2 == 1); Field<Boolean> evenOrOdd = FieldUtils.transform(isEven, isOdd, (even, odd) -> even || odd); LifecycleUtils.lifecycle(lc -> { lc.listen(num, n -> { System.out.println("num = " + n); }); lc.listen(evenOrOdd, eoo -> { System.out.println("evenOrOdd = " + eoo); }); System.out.println(); num.setValue(20); num.setValue(41); num.setValue(56); });

Logically the value of evenOrOdd should always be true, since the number contained in num must be either even or odd. But since evenOrOdd depends on num indirectly two times, this cannot be guaranteed, since the propagation order of fields changes is not well defined.

If you run the example you'll see that, even if for a brief moment, evenOrOdd assumes the value of false.

Note that it is guaranteed that the field will eventually reach the correct state. The only thing that is not guaranteed when a field depends on another one multiple times is the fact that it won't never assume an "impossible" value.

Resolution

link

To solve this problem we can simply remove the multiple dependency by merging the transformations.

For instance, the above example is fixed like so:

import com.femastudios.dataflow.extensions.* import com.femastudios.dataflow.listen.* import com.femastudios.dataflow.util.* fun main() { val num = mutableFieldOf(10) val evenOrOdd = num.transform { it % 2 == 0 || it % 2 == 1 } lifecycle { listen(num) { println("num = $it") } listen(evenOrOdd) { println("evenOrOdd = $it") } println() num.value = 20 num.value = 41 num.value = 56 } }
MutableField<Integer> num = MutableField.of(10); Field<Boolean> evenOrOdd = num.transform(n -> n % 2 == 0 || n % 2 == 1); LifecycleUtils.lifecycle(lc -> { lc.listen(num, n -> { System.out.println("num = " + n); }); lc.listen(evenOrOdd, eoo -> { System.out.println("evenOrOdd = " + eoo); }); System.out.println(); num.setValue(20); num.setValue(41); num.setValue(56); });

As you can see in this example the value of evenOrOdd will never assume false.