Click here to Skip to main content
15,896,348 members
Articles / Programming Languages / C#

AFP: Almost Functional Programming in C#: Part 2

Rate me:
Please Sign up or sign in to vote.
4.89/5 (4 votes)
6 Mar 2013CPOL20 min read 17.5K   142   12  
Putting the programming style to the test by using it in a multi-threaded server.
#region copyright
//                Copyright Andrew Rafas 2012.
// Distributed under the Eclipse Public License, Version 1.0.
//           (See accompanying file LICENSE.txt or 
//     copy at http://www.eclipse.org/legal/epl-v10.html)
#endregion
using System;
using System.Collections.Generic;

namespace CSFP.Memoize
{
    public interface ISource
    {
        /// <summary>
        /// Returns true if the value can be calculated, or already has been calculated.
        /// When returns false, Value will throw on access (if we are an IValue[T]).
        /// </summary>
        bool IsReady { get; }
        void AddDependent(IDependent dependency, int key);
    }

    public interface IValue<T> : ISource
    {
        /// <summary>
        /// Returns the expression's value when IsReady == true. It either returns the cached 
        /// value or calculates (and possibly caches) a fresh one.
        /// </summary>
        T Value { get; }
    }

    public interface IDependent
    {
        void Notify(int readyChange, int key);
    }

    public abstract class DependentList
    {
        // we can just substract these values from _notReadyCount
        public const int BecomeNotReady = -1; // which implies that all dependencies has to invalidate the cached value
        public const int ValueChanged = 0; // was Ready and remained Ready but all dependencies has to invalidate the cached value
        public const int BecomeReady = +1; // which implies that all dependencies has to invalidate the cached value

        public abstract void NotifyAll(int readyChange);
        public abstract DependentList Add(DepKey dk);
    }

    public struct DepKey
    {
        public readonly IDependent Item1;
        public readonly int Item2;

        public DepKey(IDependent item1, int item2)
        {
            Item1 = item1;
            Item2 = item2;
        }
    }

    public class MultiDependentList : DependentList
    {
        DepKey[] _list = new DepKey[4];
        int _count;

        public override void NotifyAll(int readyChange)
        {
            var list = _list;
            for (int i = 0; i < _count; ++i) {
                var d = list[i];
                d.Item1.Notify(readyChange, d.Item2);
            }
        }

        public override DependentList Add(DepKey dk)
        {
            if (_count >= _list.Length) {
                var newlist = new DepKey[_list.Length * 2];
                Array.Copy(_list, newlist, _list.Length);
                _list = newlist;
            }
            _list[_count] = dk;
            ++_count;
            return this;
        }
    }

    public class SingleDependentList : DependentList
    {
        DepKey d;

        public SingleDependentList(DepKey d)
        {
            this.d = d;
        }

        public override void NotifyAll(int readyChange)
        {
            d.Item1.Notify(readyChange, d.Item2);
        }

        public override DependentList Add(DepKey dk)
        {
            var l = new MultiDependentList();
            l.Add(d);
            return l.Add(dk);
        }
    }

    public class EmptyDependentList : DependentList
    {
        public static EmptyDependentList Instance = new EmptyDependentList();

        public override void NotifyAll(int readyChange)
        {
        }

        public override DependentList Add(DepKey dk)
        {
            return new SingleDependentList(dk);
        }
    }

    public static class Helper
    {
#if DEBUG
        static Dictionary<object, string> _debugInfo = new Dictionary<object, string>();
        static Dictionary<object, string> _shortDebugInfo = new Dictionary<object, string>();
        public static long CalculationCount = 0;
        public static long NotificationCount = 0;

        static string debugize(object o, object def)
        {
            if (def == null)
                def = o;
            string info;
            if (_shortDebugInfo.TryGetValue(o, out info))
                return info;
            if (_debugInfo.TryGetValue(o, out info))
                return info;
            return def.ToString();
        }

        [System.Diagnostics.Conditional("DEBUG")]
        internal static void Log(string format, params object[] values)
        {
            for (int i = 0; i < values.Length; ++i) {
                string info;
                if (_debugInfo.TryGetValue(values[i], out info))
                    values[i] = info;
            }
            string line = string.Format(format, values);
            System.Diagnostics.Trace.WriteLine(line);
        }

        /// <summary>
        /// Sets up the debuginfo which is used for tracing calculations.
        /// </summary>
        [System.Diagnostics.Conditional("DEBUG")]
        public static void Debug(object o, string info, string shortName = null)
        {
            if (o is Dependent) {
                var d = (Dependent)o;
                var sb = new System.Text.StringBuilder();
                sb.AppendFormat("{0}(", info);
                for (int i = 0; i < d.SourceList.Length; ++i) {
                    if (i != 0)
                        sb.Append(", ");
                    sb.Append(debugize(d.SourceList[i], "?"));
                }
                sb.Append(")");
                _debugInfo[o] = sb.ToString();
            } else {
                _debugInfo[o] = info;
            }
            if (shortName != null)
                _shortDebugInfo[o] = shortName;
        }
#else
        public static void Debug(object o, string info, string shortName = null) { }
#endif
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
New Zealand New Zealand
I am a senior software developer with almost 20 years of experience. I have extensive knowledge of C#, C++ and Assembly languages, working mainly on Windows and embedded systems. Outside of work I am interested in a wider variety of technologies, including learning 20 programming languages, developing Linux kernel drivers or advocating Functional Programming recently.

Comments and Discussions