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<T>
interface and used that to help test this, but that's not possible.
Anyway, here's the code:
// State a
class State<TResult>
{
// State a t
public struct StateData<TState>
{
public TResult result;
public TState state;
}
// return :: t -> m t
public static StateData<TState> Return<TState>(TState state)
{
StateData<TState> monadicValue = new StateData<TState>();
monadicValue.result = default(TResult);
monadicValue.state = state;
return monadicValue;
}
// (>>=) :: m t -> (t -> m u) -> m u
public static StateData<TNewState> Bind<TState, TNewState>(StateData<TState> monadicValue, Func<TState, StateData<TNewState>> func)
{
StateData<TNewState> 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<TNewState> Lift<TState, TNewState>(StateData<TState> monadicValue, Func<TState, TNewState> func)
{
StateData<TNewState> newMonadicValue = new StateData<TNewState>();
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 State<TResult>
{
// State a t
public class StateData<TState>
{
public TResult Result { get; private set; }
public TState State { get; private set; }
public StateData(TResult result, TState state)
{
this.Result = result;
this.State = state;
}
}
// return :: a -> m a
public static StateData<TState> Return<TState>(TState state)
{
return 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)
{
return 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)
{
return Return(func(monadicValue.State));
}
}
I tested this thing by making tic tac toe. The type of TResult is enum Player { Nobody, X, O }
and the type of TState is GameState
, which knows the board state and the next player. Here's the Main function for your enjoyment:
static void Main(string[] args)
{
Console.WriteLine("Use the number pad to select a location.");
State<Player>.StateData<GameState> game = State<Player>.Return<GameState>(new GameState());
while (game.Result == Player.Nobody)
{
game = State<Player>.Lift<GameState, GameState>(game, nextPlayerMove);
game = State<Player>.Lift<GameState, GameState>(game, drawBoard);
game = State<Player>.Bind<GameState, GameState>(game, findWinner);
}
Console.WriteLine(game.Result.ToString() + " won.");
}