Clean and type-safe state machine implementation in a statically typed language? Clean and type-safe state machine implementation in a statically typed language? python python

Clean and type-safe state machine implementation in a statically typed language?


In Haskell, the idiom for this is just to go ahead and execute the next state:

type StateMachine = IO ()a, b, c :: StateMachinea = print "a()" >> bb = print "b()" >> cc = print "c()" >> a

You need not worry that this will overflow a stack or anything like that. If you insist on having states, then you should make the data type more explicit:

data PossibleStates = A | B | Ctype StateMachine = PossibleStates -> IO PossibleStatesmachine A = print "a()" >> return Bmachine B = print "b()" >> return Cmachine C = print "c()" >> return A

You can then get compiler warnings about any StateMachine that forgot some states.


If you use newtype instead of data, you don't incur any overhead. Also, you can wrap each state's function at the point of definition, so the expressions that use them don't have to:

import Control.Monadnewtype State = State { runState :: IO State }a :: Statea = State $ print "a()" >> return bb :: Stateb = State $ print "b()" >> return cc :: Statec = State $ print "c()" >> return arunMachine :: State -> IO ()runMachine s = runMachine =<< runState smain = runMachine a

Edit: it struck me that runMachine has a more general form; a monadic version of iterate:

iterateM :: Monad m => (a -> m a) -> a -> m [a]iterateM f a = do { b <- f a                  ; as <- iterateM f b                  ; return (a:as)                  }main = iterateM runState a

Edit: Hmm, iterateM causes a space-leak. Maybe iterateM_ would be better.

iterateM_ :: Monad m => (a -> m a) -> a -> m ()iterateM_ f a = f a >>= iterateM_ fmain = iterateM_ runState a

Edit: If you want to thread some state through the state machine, you can use the same definition for State, but change the state functions to:

a :: Int -> Statea i = State $ do{ print $ "a(" ++ show i ++ ")"                ; return $ b (i+1)                }b :: Int -> Stateb i = State $ do{ print $ "b(" ++ show i ++ ")"                ; return $ c (i+1)                }c :: Int -> Statec i = State $ do{ print $ "c(" ++ show i ++ ")"                ; return $ a (i+1)                }main = iterateM_ runState $ a 1


The problem with your Haskell code is, that type only introduces a synonym, which is quite similar to what typedef in C does. One important restriction is, that the expansion of the type must be finite, you can't give a finite expansion of your state machine. A solution is using a newtype: A newtype is a wrapper that does only exist for the type checker; there is absolutely zero overhead (excluded stuff that occurs because of generalization that can't be removed). Here is your signature; it typechecks:

newtype FN = FN { unFM :: (IO FN) }

Please note, that whenever you want to use an FN, you have to unpack it first using unFN. Whenever you return a new function, use FN to pack it.