如何将索引的monad用作FSM?

时间:2019-04-23 01:00:45

标签: haskell monads state-machine

我正在尝试创建一个简单的状态机,该状态机在输入有效时会更改颜色状态: Red-> Green-> Blue-> Red ...

我希望能够显式定义状态转换。阅读What is indexed monad?和Stephen Diehl的What I Wish I Knew之后,索引的monad似乎是我所需要的。到目前为止,我有以下代码:

import Prelude hiding ( return )

newtype IState i o a = IState { runIState :: i -> (a, o) }

execIState :: IState i o a -> i -> o
execIState st i = snd $ runIState st i

return :: a -> IState s s a
return a = IState $ \s -> (a,s)

put :: o -> IState i o ()
put o = IState $ const ((), o)

data Red   = Red
data Green = Green
data Blue  = Blue

blueToRed :: IState Blue Red ()
blueToRed = put Red

redToGreen :: IState Red Green ()
redToGreen = put Green

greenToBlue :: IState Green Blue ()
greenToBlue = put Blue

newtype Color a = Color a

colorChange
  :: Color a
  -> Bool
  -> Color a
colorChange s@(Color c) valid = Color $ flip execIState c $ case s of
  (Color Red)   | valid -> redToGreen
  (Color Green) | valid -> greenToBlue
  (Color Blue)  | valid -> blueToRed
  _ -> return ()

此代码给出错误:

Couldn't match type `Blue' with `Green'
      Expected type: IState a Green ()
        Actual type: IState Green Blue ()
    * In the expression: greenToBlue
      In a case alternative: (Color Green) | valid -> greenToBlue
      In the second argument of `($)', namely
        `case s of
           (Color Red) | valid -> redToGreen
           (Color Green) | valid -> greenToBlue
           (Color Blue) | valid -> blueToRed
           _ -> return ()'
   |
39 |   (Color Green) | valid -> greenToBlue


Couldn't match type `Red' with `Green'
      Expected type: IState a Green ()
        Actual type: IState Blue Red ()
    * In the expression: blueToRed
      In a case alternative: (Color Blue) | valid -> blueToRed
      In the second argument of `($)', namely
        `case s of
           (Color Red) | valid -> redToGreen
           (Color Green) | valid -> greenToBlue
           (Color Blue) | valid -> blueToRed
           _ -> return ()'
   |
40 |   (Color Blue)  | valid -> blueToRed

我知道RedGreenBlue的类型是不同的。但是错误对我来说没有意义,为什么编译器会在IState a Green ()时期望greenToBlue :: IState Green Blue ()?在我看来,期望所有类型都“匹配”第一个case模式redToGreen。如何解决此问题以创建我的状态传递函数? “什么是索引的monad?”该帖子使用了GADT,所以我认为这可能会有所帮助,但是我无法在该帖子中获得示例,我以前也没有使用GADT,只是阅读了有关它们的内容。

请注意,这对于调试和学习非常简单,我计划在我的FSM复杂性增加时使用它。

澄清:如果状态传递函数未保留状态机结构,我希望编译器给出错误信息。假设我将状态机结构定义为:Red-> Green-> Blue-> Red ...但是如果我不小心更改了colorChange函数因此Red-> Blue,编译器应发出错误,因为这违反了Green必须遵循Red的状态机结构。

1 个答案:

答案 0 :(得分:2)

我建议保持简单。

data Color = Red | Green | Blue

colorChange :: Color -> Bool -> Color
colorChange s     False = s
colorChange Red   _     = Green
colorChange Green _     = Blue
colorChange Blue  _     = Red