Category Theory via C# (19) More Monad: State< , > Monad

[LINQ via C# series]

[Category Theory via C# series]

Latest version: https://weblogs.asp.net/dixin/category-theory-via-csharp-8-more-linq-to-monads

C#/.NET state machines

State machine (or finite state machine) represents a abstract machine with one state or a number of state.  C# use state machine a lot. For example:

.NET has a lot of built-in state machines too:

etc.

State pattern in object-oriented programming

State pattern is a typical way to implement state machine. The following picture is stolen from Wikipedia:

Traffic light state machine

A very simple example of (finite) state machine is traffic light. Assume a traffic light state machine has 3 state:

  • It starts with green state, and stays green for 3 seconds
  • Then it mutates to yellow state for 1 second
  • Then it mutates to red state, for 2 seconds

The code will just follow above diagram. Here are the states’ definitions:

public interface ITrafficLightState // State
{
    Task Handle(TrafficLightStateMachine light);
}

public class GreenState : ITrafficLightState // ConcreteStateA
{
    public async Task Handle(TrafficLightStateMachine light)
    {
        TraceHelper.TypeName(typeof(GreenState));
        await Task.Delay(3000);
        await light.MoveNext(new YellowState());
    }
}

public class YellowState : ITrafficLightState // ConcreteStateB
{
    public async Task Handle(TrafficLightStateMachine light)
    {
        TraceHelper.TypeName(typeof(YellowState));
        await Task.Delay(1000);
        await light.MoveNext(new RedState());
    }
}

public class RedState : ITrafficLightState // ConcreteStateC
{
    public async Task Handle(TrafficLightStateMachine light)
    {
        TraceHelper.TypeName(typeof(RedState));
        await Task.Delay(2000);
        // await light.MoveNext(new GreenState());
    }
}

where TraceHelper.TypeName is just:

public static partial class TraceHelper
{
    public static Unit TypeName(Type type)
    {
        Trace.WriteLine($"{DateTime.Now.ToString("o", CultureInfo.InvariantCulture)}: {type.Name}");
        return null;
    }
}

Notice Trace.TypeName and all Handle method implementations have side effects (write trace messages). And, in typical C# programming and OOP, side effect is not specially managed.

The state machine will be:

public class TrafficLightStateMachine
{
    public ITrafficLightState State { get; private set; }

    public async Task MoveNext(ITrafficLightState state = null)
    {
        this.State = state ?? new GreenState();
        await this.State.Handle(this);
    }
}

Notice the state machine is mutable. The underlined code updates the state of the state machine.

Running the state machine:

new TrafficLightStateMachine().MoveNext().Wait();

can result the following trace message:

2015-05-24T16:19:08.2431705-07:00 - GreenState
2015-05-24T16:19:11.2530920-07:00 - YellowState
2015-05-24T16:19:12.2549302-07:00 - RedState

State<> monad

In purely functional programming, objects are immutable, state cannot just be updated when changing. State monad can be used to thread a state parameter through a sequence of functions to represent the state updating.

This is the definition of state monad:

// State<T, TState> is alias of Func<TState, Lazy<T, TState>>
public delegate Lazy<T, TState> State<T, TState>(TState state);

As usual, its SelectMany will be defined firstly:

[Pure]
public static partial class StateExtensions
{
    // Required by LINQ.
    public static State<TResult, TState> SelectMany<TSource, TState, TSelector, TResult>
        (this State<TSource, TState> source,
         Func<TSource, State<TSelector, TState>> selector,
         Func<TSource, TSelector, TResult> resultSelector) =>
            state => new Lazy<TResult, TState>(() =>
                {
                    Lazy<TSource, TState> sourceResult = source(state);
                    Lazy<TSelector, TState> selectorResult = selector(sourceResult.Value1)(sourceResult.Value2);
                    return Tuple.Create(
                        resultSelector(sourceResult.Value1, selectorResult.Value1),
                        selectorResult.Value2);
                });

    // Not required, just for convenience.
    public static State<TResult, TState> SelectMany<TSource, TState, TResult>
        (this State<TSource, TState> source, Func<TSource, State<TResult, TState>> selector) =>
            source.SelectMany(selector, Functions.False);
}

so that:

// [Pure]
public static partial class StateExtensions
{
    // η: T -> State<T, TState>
    public static State<T, TState> State<T, TState>
        (this T value) => state => new Lazy<T, TState>(value, state);

    // η: T -> State<T, TState>
    public static State<T, TState> State<T, TState>
        (this T value, Func<TState, TState> newState) =>
            oldState => new Lazy<T, TState>(value, newState(oldState));

    // φ: Lazy<State<T1, TState>, State<T2, TState>> => State<Defer<T1, T2>, TState>
    public static State<Lazy<T1, T2>, TState> Binary<T1, T2, TState>
        (this Lazy<State<T1, TState>, State<T2, TState>> binaryFunctor) =>
            binaryFunctor.Value1.SelectMany(
                value1 => binaryFunctor.Value2,
                (value1, value2) => new Lazy<T1, T2>(value1, value2));

    // ι: TUnit -> State<TUnit, TState>
    public static State<Unit, TState> Unit<TState>
        (Unit unit) => unit.State<Unit, TState>();

    // Select: (TSource -> TResult) -> (State<TSource, TState> -> State<TResult, TState>)
    public static State<TResult, TState> Select<TSource, TResult, TState>
        (this State<TSource, TState> source, Func<TSource, TResult> selector) =>
            source.SelectMany(value => selector(value).State<TResult, TState>());
}

State<> is monad, monoidal functor, and functor.

Also a few other helper functions:

// [Pure]
public static partial class StateExtensions
{
    public static TSource Value<TSource, TState>
        (this State<TSource, TState> source, TState state) => source(state).Value1;

    public static TState State<T, TState>
        (this State<T, TState> source, TState state) => source(state).Value2;
}

[Pure]
public static class State
{
    public static State<TState, TState> Get<TState>
        () => state => new Lazy<TState, TState>(state, state);

    public static State<TState, TState> Set<TState>
        (TState newState) => oldState => new Lazy<TState, TState>(oldState, newState);

    public static State<TState, TState> Set<TState>
        (Func<TState, TState> newState) => oldState => new Lazy<TState, TState>(oldState, newState(oldState));
}

Traffic light state machine with State<> monad and LINQ

Now everything becomes functions. This is the definition of the traffic light state:

public delegate IO<Task<TrafficLightState>> TrafficLightState();

Not interface any more.

And each state is a pure function of above type:

// Impure.
public static partial class StateQuery
{
    [Pure]
    public static IO<Task<TrafficLightState>> GreenState
        () =>
            from _ in TraceHelper.Log(nameof(GreenState))
            select (from __ in Task.Delay(TimeSpan.FromSeconds(3))
                    select new TrafficLightState(YellowState));

    [Pure]
    public static IO<Task<TrafficLightState>> YellowState
        () =>
            from _ in TraceHelper.Log(nameof(YellowState))
            select (from __ in Task.Delay(TimeSpan.FromSeconds(1))
                    select new TrafficLightState(RedState));

    [Pure]
    public static IO<Task<TrafficLightState>> RedState
        () =>
            from _ in TraceHelper.Log(nameof(RedState))
            select (from __ in Task.Delay(TimeSpan.FromSeconds(2))
                    select default(TrafficLightState));
}

where Trace.Log is a pure function too:

[Pure]
public static partial class TraceHelper
{
    public static IO<Unit> Log
        (string log) =>
            () =>
                {
                    Trace.WriteLine($"{DateTime.Now.ToString("o", CultureInfo.InvariantCulture)} - {log}");
                    return null;
                };
}

Please also notice Task.Delay returns a Task (not Task<>). As mentioned in an earlier part, Task can be viewed as Task<Unit>, a special case of Task<>. So the LINQ syntax works for Task.

The state machine is also pure function:

// Impure.
public static partial class StateQuery
{
    [Pure]
    public static State<Unit, IO<Task<TrafficLightState>>> MoveNext
        () =>
            ((Unit)null).State<Unit, IO<Task<TrafficLightState>>>(state => async () =>
                {
                    TrafficLightState next = await (state ?? GreenState())();
                    return next == null ? null : await next()();
                });

    [Pure]
    public static IO<Task<TrafficLightState>> TrafficLight(IO<Task<TrafficLightState>> state = null)
    {
        State<Unit, IO<Task<TrafficLightState>>> query =
            from green in MoveNext()
            from yellow in MoveNext()
            from red in MoveNext()
            select (Unit)null; // Deferred and lazy.
        return query.State(state); // Final state.
    }
}

Running this state machine with State<> monad:

// Impure.
public static partial class StateQuery
{
    public static async void ExecuteTrafficLight() => await TrafficLight()();
}

will result similar trace message:

04/02/2015 20:44:30 - GreenState
04/02/2015 20:44:33 - YellowState
04/02/2015 20:44:34 - RedState

Immutable IEnumerable<T> stack

An easier example could be using a immutable IEnumerable<T> to simulate a mutable stack. Firstly, a Pop and a Push function can be implemented:

// [Pure]
public static partial class EnumerableExtensions
{
    public static Lazy<T, IEnumerable<T>> Pop<T>
        (this IEnumerable<T> source) =>
            // The execution of First is deferred, so that Pop is still pure.
            new Lazy<T, IEnumerable<T>>(source.First, () => source.Skip(1));

    public static Lazy<T, IEnumerable<T>> Push<T>
        (this IEnumerable<T> source, T value) =>
            new Lazy<T, IEnumerable<T>>(value, source.Concat(value.Enumerable()));
}

So a stateful stack can be implemented as:

// Impure.
public static partial class StateQuery
{
    [Pure]
    public static State<T, IEnumerable<T>> Pop<T>
        () => source => source.Pop();

    [Pure]
    public static State<T, IEnumerable<T>> Push<T>
        (T value) => source => source.Push(value);

    [Pure]
    public static IEnumerable<int> Stack(IEnumerable<int> state = null)
    {
        state = state ?? Enumerable.Empty<int>();
        State<IEnumerable<int>, IEnumerable<int>> query =
            from value1 in Push(1)
            from value2 in Push(2)
            from value3 in Pop<int>()
            from stack1 in State.Set(Enumerable.Range(0, 3))
            from value4 in Push(4)
            from value5 in Pop<int>()
            from stack2 in State.Get<IEnumerable<int>>()
            select stack2;
        return query.Value(state);
    }
}

The above functions are all pure functions, and IEnumerable<int> is immutable. They clearly demonstrated how State<> monad simulates the state updating - after each call of Push, Pop, or Set, a new IEnumerable<T> is created to pass to the next function in the sequence.

[TestClass]
public class StackTests
{
    [TestMethod]
    public void StateMachineTest()
    {
        IEnumerable<int> expected = Enumerable.Range(0, 3).Push(4).Value2.Pop().Value2;
        IEnumerable<int> actual = StateQuery.Stack();
        EnumerableAssert.AreEqual(expected, actual);
    }
}

Monad laws, and unit tests

public partial class MonadTests
{
    [TestMethod]
    public void StateTest()
    {
        bool isExecuted1 = false;
        bool isExecuted2 = false;
        Func<State<int, string>> f1 = () => 1.State<int, string>(
            state => { isExecuted1 = true; return state + "a"; });
        Func<int, Func<int, Func<string, int>>> f2 =
            x => y => z => { isExecuted2 = true; return x + y + z.Length; };
        State<int, string> query1 = from x in f1()
                                    from _ in State.Set(x.ToString(CultureInfo.InvariantCulture))
                                    from y in 2.State<int, string>(state => "b" + state)
                                    from z in State.Get<string>()
                                    select f2(x)(y)(z);
        Assert.IsFalse(isExecuted1); // Deferred and lazy.
        Assert.IsFalse(isExecuted2); // Deferred and lazy.
        Lazy<int, string> result1 = query1("state"); // Execution.
        Assert.AreEqual(1 + 2 + ("b" + "1").Length, result1.Value1);
        Assert.AreEqual("b" + "1", result1.Value2);
        Assert.IsTrue(isExecuted1);
        Assert.IsTrue(isExecuted2);

        // Monad law 1: m.Monad().SelectMany(f) == f(m)
        Func<int, State<int, string>> addOne = x => (x + 1).State<int, string>();
        State<int, string> left = 1.State<int, string>().SelectMany(addOne);
        State<int, string> right = addOne(1);
        Assert.AreEqual(left.Value("a"), right.Value("a"));
        Assert.AreEqual(left.State("a"), right.State("a"));
        // Monad law 2: M.SelectMany(Monad) == M
        State<int, string> M = 1.State<int, string>();
        left = M.SelectMany(StateExtensions.State<int, string>);
        right = M;
        Assert.AreEqual(left.Value("a"), right.Value("a"));
        Assert.AreEqual(left.State("a"), right.State("a"));
        // Monad law 3: M.SelectMany(f1).SelectMany(f2) == M.SelectMany(x => f1(x).SelectMany(f2))
        Func<int, State<int, string>> addTwo = x => (x + 2).State<int, string>();
        left = M.SelectMany(addOne).SelectMany(addTwo);
        right = M.SelectMany(x => addOne(x).SelectMany(addTwo));
        Assert.AreEqual(left.Value("a"), right.Value("a"));
        Assert.AreEqual(left.State("a"), right.State("a"));
    }
}

9 Comments

  • https://ma-study.blogspot.com/

  • It's too bad to check your article late. I wonder what it would be if we met a little faster. I want to exchange a little more, but please visit my site Keo nha cai and leave a message!!

  • Unfortunately, you checked your article too late. If we met a bit sooner, I wonder what it would be like. Please stop by my website, Keo nha cai, and leave a message if you'd like to continue our conversation.

  • I've been troubled for several days with this topic. But by chance looking at your post solved my problem! I will leave my blog, so when would you like to visit it?

  • I am very impressed with your writing <a href="https://google.je/url?sa=t&url=https%3A%2F%2Fwww.mtclean.blog/">baccaratcommunity</a> I couldn't think of this, but it's amazing! I wrote several posts similar to this one, but please come and see!

  • Hello ! I am the one who writes posts on these topics <a href="https://google.it/url?sa=t&url=https%3A%2F%2Fwww.mtclean.blog/">casinocommunity</a> I would like to write an article based on your article. When can I ask for a review?

  • I was looking for another article by chance and found your article <a href="https://google.is/url?sa=t&url=https%3A%2F%2Fwww.mtclean.blog/">majorsite</a> I am writing on this topic, so I think it will help a lot. I leave my blog address below. Please visit once.

  • I came to this site with the introduction of a friend around me and I was very impressed when I found your writing. I'll come back often after bookmarking! <a href="https://google.iq/url?sa=t&url=https%3A%2F%2Fwww.mtclean.blog/">bitcoincasino</a>

  • International students typically face a multitude of new challenges. Language barriers, varying academic norms, and shifts in educational methodologies can make the academic path complex. These factors combined may lead to stress and discomfort as international students adapt to their new environment, affecting their academic performance.

Add a Comment

As it will appear on the website

Not displayed

Your website