Building APIs, Part 3: Controlling Code Complexity

This is a talk I gave for a developer training session recently. This is the third part of the series, but the first two parts are not (yet) posted. The prior two were more interactive sessions; as I adapt them for review, I will post them here.

Part 1 has now been posted.

Building APIs, Part 3: Controlling Code Complexity

prezi

The talk is structured around C# examples, but is relevant for most any language. The examples themselves should be trivial to grok for any programmer, regardless of C# experience.

Some external code samples are mentioned; they are provided below.

Code Examples

InputObject and OutputObject

    public class InputObject
    {
        public List<int> InputSet { get; set; }
        public int FilterValue { get; set; }
    }

    public class OutputObject
    {
        public List<int> Result { get; set; }
        public List<int> BadSet { get; set; }
        public string FailureReason { get; set; }
    }

MaybeM

    public class MaybeM<T>
    {
        public bool HasValue { get; private set; }
        public T Value { get; private set; } // You can call this "lifting out" the value

        public MaybeM()
        {
            HasValue = false;
            Value = default(T);
        }

        public MaybeM(T value)
        {
            HasValue = true;
            Value = value;
        }
    }

MaybeM.Bind

    public class MaybeM<T>
    {
        public MaybeM<T> Bind(Func<T, MaybeM<T>> function)
        {
            if (!HasValue) return this;
            return function(Value);
        }
    }

EitherM

    public class EitherM<TSuccess, TFailure>
    {
        public bool IsSuccess { get; private set; }
        public TSuccess Success { get; private set; }
        public TFailure Failure { get; private set; }

        public EitherM(TSuccess success)
        {
            IsSuccess = true;
            Success = success;
            Failure = default(TFailure);
        }

        public EitherM(TFailure failure)
        {
            IsSuccess = false;
            Success = default(TSuccess);
            Failure = failure;
        }

        public EitherM<TSuccess, TFailure> Bind(Func<TSuccess, EitherM<TSuccess, TFailure>> function)
        {
            if (!IsSuccess) return this;
            return function(Success);
        }
    }

ListM

    public class ListM<T>
    {
        public List<T> Value { get; private set; }

        public ListM(List<T> value)
        {
            Value = value;
        }

        public ListM<T> Bind(Func<T, ListM<T>> function)
        {
            // NOT optimized!

            // Step 1: Generate the intermediate ListM results
            IEnumerable<ListM<T>> intermediate = Value.Select(function);

            // Step 2: Collapse the intermediate results into a single List
            List<T> result = new List<T>();
            intermediate.ToList().ForEach(x => result.AddRange(x.Value));

            // Return the result wrapped in a ListM, to keep the paradigm intact
            return new ListM<T>(result);
        }
    }