Exemple #1
0
 public static IDisposable Reaction <T>(
     AtomPull <T> pull,
     Action <T> reaction,
     IEqualityComparer <T> comparer      = null,
     bool fireImmediately                = false,
     Action <Exception> exceptionHandler = null)
 {
     return(Reaction(pull, (value, _) => reaction(value), comparer, fireImmediately, exceptionHandler));
 }
Exemple #2
0
 /// <summary>
 /// Creates an atom that compute its value by a function.<br/>
 /// </summary>
 /// <remarks>
 /// Computed values can be used to derive information from other atoms.
 /// They evaluate lazily, caching their output and only recomputing
 /// if one of the underlying atoms has changed. If they are not observed
 /// by anything, they suspend entirely.<br/>
 /// <br/>
 /// Conceptually, they are very similar to formulas in spreadsheets,
 /// and can't be underestimated. They help in reducing the amount of state
 /// you have to store and are highly optimized. Use them wherever possible.
 /// </remarks>
 /// <param name="pull">Function for pulling value.</param>
 /// <param name="keepAlive">Should an atom keep its value cached when there are no subscribers?</param>
 /// <param name="requiresReaction">Should an atom print warnings when its values are tried to be read outside of the reaction?</param>
 /// <param name="callbacks">Atom lifetime callbacks.</param>
 /// <param name="comparer">Value comparer used for reconciling.</param>
 /// <param name="debugName">Debug name for this atom.</param>
 /// <typeparam name="T">Atom value type.</typeparam>
 /// <returns>Created atom.</returns>
 /// <example>
 ///
 /// var a = Atom.Value(1);
 /// var b = Atom.Value(2);
 ///
 /// var sum = Atom.Computed(() => a.Value + b.Value);
 ///
 /// Debug.Log(sum.Value);
 ///
 /// </example>
 public static Atom <T> Computed <T>(
     AtomPull <T> pull,
     bool keepAlive                 = false,
     bool requiresReaction          = false,
     IAtomCallbacks callbacks       = null,
     IEqualityComparer <T> comparer = null,
     string debugName               = null)
 {
     return(new ComputedAtom <T>(debugName, pull, null, keepAlive, requiresReaction, callbacks, comparer));
 }
Exemple #3
0
 public static Atom <T> Computed <T>(
     AtomPull <T> pull,
     bool keepAlive                 = false,
     bool requiresReaction          = false,
     Action onActive                = null,
     Action onInactive              = null,
     IEqualityComparer <T> comparer = null)
 {
     return(new ComputedAtom <T>(pull, null, keepAlive, requiresReaction, onActive, onInactive, comparer));
 }
Exemple #4
0
 internal ComputedAtom(
     [NotNull] AtomPull <T> pull,
     AtomPush <T> push              = null,
     bool keepAlive                 = false,
     bool requiresReaction          = false,
     Action onActive                = null,
     Action onInactive              = null,
     IEqualityComparer <T> comparer = null)
     : base(keepAlive, onActive, onInactive)
 {
     _pull             = pull ?? throw new ArgumentNullException(nameof(pull));
     _push             = push;
     _comparer         = comparer ?? EqualityComparer <T> .Default;
     _requiresReaction = requiresReaction;
 }
Exemple #5
0
 internal ComputedAtom(
     string debugName,
     [NotNull] AtomPull <T> pull,
     AtomPush <T> push              = null,
     bool keepAlive                 = false,
     bool requiresReaction          = false,
     IAtomCallbacks callbacks       = null,
     IEqualityComparer <T> comparer = null)
     : base(debugName, keepAlive, callbacks)
 {
     _pull             = pull ?? throw new ArgumentNullException(nameof(pull));
     _push             = push;
     _comparer         = comparer ?? EqualityComparer <T> .Default;
     _requiresReaction = requiresReaction;
 }
Exemple #6
0
        /// <summary>
        /// Creates an reaction that takes two functions:
        /// the first, data function, is tracked and returns the data that is used
        /// as input for the second, effect function. It is important to note that
        /// the side effect only reacts to data that was accessed in the data function,
        /// which might be less than the data that is actually used in the effect function.
        ///
        /// The typical pattern is that you produce the things you need in your side effect
        /// in the data function, and in that way control more precisely when the effect triggers.
        /// </summary>
        /// <param name="reaction">A data function.</param>
        /// <param name="effect">A side effect function.</param>
        /// <param name="exceptionHandler">A function that called when an exception is thrown while computing an reaction.</param>
        /// <param name="comparer">Data comparer used for reconciling.</param>
        /// <param name="fireImmediately">Should the side effect runs once when you create a reaction itself? Default value is true.</param>
        /// <param name="debugName">Debug name for this reaction.</param>
        /// <typeparam name="T">Reaction data type.</typeparam>
        /// <returns>Created reaction.</returns>
        /// <example>
        ///
        /// var counter = Atom.Value(1);
        ///
        /// var reaction = Atom.Reaction(
        ///     () => counter.Value,
        ///     val => Debug.Log("Counter: " + val)
        /// );
        ///
        /// </example>

        public static Reaction Reaction <T>(
            AtomPull <T> reaction,
            Action <T> effect,
            Action <Exception> exceptionHandler = null,
            IEqualityComparer <T> comparer      = null,
            bool fireImmediately = true,
            string debugName     = null)
        {
            return(Reaction(
                       reaction: reaction,
                       effect: (value, _) => effect(value),
                       exceptionHandler: exceptionHandler,
                       comparer: comparer,
                       fireImmediately: fireImmediately,
                       debugName: debugName
                       ));
        }
Exemple #7
0
        /// <summary>
        /// Creates an reaction that takes two functions:
        /// the first, data function, is tracked and returns the data that is used
        /// as input for the second, effect function. It is important to note that
        /// the side effect only reacts to data that was accessed in the data function,
        /// which might be less than the data that is actually used in the effect function.
        ///
        /// The typical pattern is that you produce the things you need in your side effect
        /// in the data function, and in that way control more precisely when the effect triggers.
        /// </summary>
        /// <param name="reaction">A data function.</param>
        /// <param name="effect">A side effect function.</param>
        /// <param name="exceptionHandler">A function that called when an exception is thrown while computing an reaction.</param>
        /// <param name="comparer">Data comparer used for reconciling.</param>
        /// <param name="fireImmediately">Should the side effect runs once when you create a reaction itself? Default value is true.</param>
        /// <param name="debugName">Debug name for this reaction.</param>
        /// <typeparam name="T">Reaction data type.</typeparam>
        /// <returns>Created reaction.</returns>
        /// <example>
        ///
        /// var counter = Atom.Value(1);
        ///
        /// var reaction = Atom.Reaction(
        ///     () => counter.Value,
        ///     (val, reactionHandle) =>
        ///     {
        ///         Debug.Log("Counter: " + val);
        ///         if (val == 10)
        ///         {
        ///             reactionHandle.Deactivate();
        ///         }
        ///     }
        /// );
        ///
        /// </example>
        public static Reaction Reaction <T>(
            AtomPull <T> reaction,
            Action <T, Reaction> effect,
            Action <Exception> exceptionHandler = null,
            IEqualityComparer <T> comparer      = null,
            bool fireImmediately = true,
            string debugName     = null)
        {
            var  valueAtom = Computed(reaction, comparer: comparer);
            bool firstRun  = true;

            Reaction atom = null;

            atom = new ReactionAtom(debugName, () =>
            {
                var value = valueAtom.Value;

                using (NoWatch)
                {
                    if (firstRun)
                    {
                        firstRun = false;

                        if (!fireImmediately)
                        {
                            return;
                        }
                    }

                    // ReSharper disable once AccessToModifiedClosure
                    effect(value, atom);
                }
            }, exceptionHandler);

            atom.Activate();
            return(atom);
        }
Exemple #8
0
        public static IDisposable Reaction <T>(
            AtomPull <T> pull,
            Action <T, IDisposable> reaction,
            IEqualityComparer <T> comparer      = null,
            bool fireImmediately                = false,
            Action <Exception> exceptionHandler = null)
        {
            var  valueAtom = Computed(pull, comparer: comparer);
            bool firstRun  = true;

            ReactionAtom atom = null;

            atom = new ReactionAtom(() =>
            {
                var value = valueAtom.Value;

                using (NoWatch)
                {
                    if (firstRun)
                    {
                        firstRun = false;

                        if (!fireImmediately)
                        {
                            return;
                        }
                    }

                    // ReSharper disable once AccessToModifiedClosure
                    reaction(value, atom);
                }
            }, exceptionHandler);

            atom.Get();
            return(atom);
        }
Exemple #9
0
 public static ComputedAtom <T> Create <T>(string debugName, AtomPull <T> pull)
 {
     return(new ComputedAtom <T>(debugName, pull));
 }