HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

C# state monad implementation

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
implementationstatemonad

Problem

I want to know whether this thing I wrote in C# is a correct implementation of the state monad.

I've used it in code and it does what I expect it to do, but I'm still not quite sure if I'm doing this right, or if I just pulled something out of my ear and called it a state monad.

I would have made an IMonad interface and used that to help test this, but that's not possible.

Anyway, here's the code:

// State a
class State
{
    // State a t
    public struct StateData
    {
        public TResult result;
        public TState state;
    }

    // return :: t -> m t
    public static StateData Return(TState state) 
    {
        StateData monadicValue = new StateData();

        monadicValue.result = default(TResult);
        monadicValue.state = state;

        return monadicValue;
    }

    // (>>=) :: m t -> (t -> m u) -> m u
    public static StateData Bind(StateData monadicValue, Func> func)
    {
        StateData newMonadicValue = func(monadicValue.state);

        return newMonadicValue;
    }

    // liftM :: (t -> u) -> m t -> m u
    // liftM :: m t -> (t -> u) -> m u -- looks more similar to bind this way
    public static StateData Lift(StateData monadicValue, Func func)
    {
        StateData newMonadicValue = new StateData();

        newMonadicValue.result = monadicValue.result;
        newMonadicValue.state = func(monadicValue.state);

        return newMonadicValue;
    }
}


EDIT: You're not supposed to use mutable structs. Here in particular it's helpful to adhere to that, because the state object in the state monad isn't supposed to be mutable either. Ironically, I had to change StateData from a struct to a class to get the compiler to quit complaining about the constructor. Nonetheless, it is now immutable.

Also, I changed the Lift function so that it no longer passes through stale TResults.

Based on the type signatures and how quaint the code has become, I'm pretty sure this thing is indeed a state monad.

```
// State a
class

Solution

Mutable structs in C#/.NET are evil for many reasons you can Google. Rewrite as such:

internal class State
{
    // State a t
    public struct StateData
    {
        public StateData(TResult result, TState state)
        {
            this.Result = result;
            this.State = state;
        }

        public TResult Result { get; }

        public TState State { get; }
    }

    // return :: t -> m t
    public static StateData Return(TState state) => new StateData(default(TResult), state);

    // (>>=) :: m t -> (t -> m u) -> m u
    public static StateData Bind(
        StateData monadicValue,
        Func> func) => func(monadicValue.State);

    // liftM :: (t -> u) -> m t -> m u
    // liftM :: m t -> (t -> u) -> m u -- looks more similar to bind this way
    public static StateData Lift(
        StateData monadicValue,
        Func func) => new StateData(monadicValue.Result, func(monadicValue.State));
}

Code Snippets

internal class State<TResult>
{
    // State a t
    public struct StateData<TState>
    {
        public StateData(TResult result, TState state)
        {
            this.Result = result;
            this.State = state;
        }

        public TResult Result { get; }

        public TState State { get; }
    }

    // return :: t -> m t
    public static StateData<TState> Return<TState>(TState state) => new StateData<TState>(default(TResult), state);

    // (>>=) :: m t -> (t -> m u) -> m u
    public static StateData<TNewState> Bind<TState, TNewState>(
        StateData<TState> monadicValue,
        Func<TState, StateData<TNewState>> func) => func(monadicValue.State);

    // liftM :: (t -> u) -> m t -> m u
    // liftM :: m t -> (t -> u) -> m u -- looks more similar to bind this way
    public static StateData<TNewState> Lift<TState, TNewState>(
        StateData<TState> monadicValue,
        Func<TState, TNewState> func) => new StateData<TNewState>(monadicValue.Result, func(monadicValue.State));
}

Context

StackExchange Code Review Q#23559, answer score: 2

Revisions (0)

No revisions yet.