Tuesday, 20 September 2016

State machines

Someone on the internet recently asserted that F# is “atrocious” for implementing state machines compared to C#. I just Googled C# state machine and found this. I translated the example into the following F# code:

type State = Inactive | Active | Paused | Exited
type Transition = Begin | End | Pause | Resume | Exit
 
let move = function
  | Inactive, Begin -> Active
  | (Active | Paused), End -> Inactive
  | Active, Pause -> Paused
  | Paused, Resume -> Active
  | Inactive, Exit -> Exited
  | state, transition -> failwithf "Invalid transition: %A -> %A" state transition

Here is their original C# for comparison:

using System;
using System.Collections.Generic;
 
namespace Juliet
{
    public enum ProcessState
    {
        Inactive,
        Active,
        Paused,
        Terminated
    }
 
    public enum Command
    {
        Begin,
        End,
        Pause,
        Resume,
        Exit
    }
 
    public class Process
    {
        class StateTransition
        {
            readonly ProcessState CurrentState;
            readonly Command Command;
 
            public StateTransition(ProcessState currentState, Command command)
            {
                CurrentState = currentState;
                Command = command;
            }
 
            public override int GetHashCode()
            {
                return 17 + 31 * CurrentState.GetHashCode() + 31 * Command.GetHashCode();
            }
 
            public override bool Equals(object obj)
            {
                StateTransition other = obj as StateTransition;
                return other != null && this.CurrentState == other.CurrentState && this.Command == other.Command;
            }
        }
 
        Dictionary<StateTransition, ProcessState> transitions;
        public ProcessState CurrentState { get; private set; }
 
        public Process()
        {
            CurrentState = ProcessState.Inactive;
            transitions = new Dictionary<StateTransition, ProcessState>
            {
                { new StateTransition(ProcessState.Inactive, Command.Exit), ProcessState.Terminated },
                { new StateTransition(ProcessState.Inactive, Command.Begin), ProcessState.Active },
                { new StateTransition(ProcessState.Active, Command.End), ProcessState.Inactive },
                { new StateTransition(ProcessState.Active, Command.Pause), ProcessState.Paused },
                { new StateTransition(ProcessState.Paused, Command.End), ProcessState.Inactive },
                { new StateTransition(ProcessState.Paused, Command.Resume), ProcessState.Active }
            };
        }
 
        public ProcessState GetNext(Command command)
        {
            StateTransition transition = new StateTransition(CurrentState, command);
            ProcessState nextState;
            if (!transitions.TryGetValue(transition, out nextState))
                throw new Exception("Invalid transition: " + CurrentState + " -> " + command);
            return nextState;
        }
 
        public ProcessState MoveNext(Command command)
        {
            CurrentState = GetNext(command);
            return CurrentState;
        }
    }
 
 
    public class Program
    {
        static void Main(string[] args)
        {
            Process p = new Process();
            Console.WriteLine("Current State = " + p.CurrentState);
            Console.WriteLine("Command.Begin: Current State = " + p.MoveNext(Command.Begin));
            Console.WriteLine("Command.Pause: Current State = " + p.MoveNext(Command.Pause));
            Console.WriteLine("Command.End: Current State = " + p.MoveNext(Command.End));
            Console.WriteLine("Command.Exit: Current State = " + p.MoveNext(Command.Exit));
            Console.ReadLine();
        }
    }
}

 

 

2 comments:

makutaku said...

Much more succinct but it's not an apple-to-apple comparison because the C# code is better prepared to load the transitions as data, whereas the transitions in F# are hardcoded.

chrisa23 said...

I have an HSM library I've worked on as a learning project for F# (https://github.com/chrisa23/FSharp.HSM). While it has a lot of room for improvement, compared to the C# library Stateless that it was initially based on, I was able to create a feature rich library in less than 300 lines of code, including a very readable builder DSL.