Haskell 如何使用索引单子作为FSM?

Haskell 如何使用索引单子作为FSM?,haskell,monads,state-machine,Haskell,Monads,State Machine,我试图创建一个简单的状态机,在输入有效时更改颜色状态: 红色->绿色->蓝色->红色 我希望能够明确定义状态转换。经过阅读和斯蒂芬·迪尔的,索引单子似乎是我需要的。到目前为止,我有以下代码: import Prelude hiding ( return ) newtype IState i o a = IState { runIState :: i -> (a, o) } execIState :: IState i o a -> i -> o execIState st

我试图创建一个简单的状态机,在输入有效时更改颜色状态:
红色
->
绿色
->
蓝色
->
红色

我希望能够明确定义状态转换。经过阅读和斯蒂芬·迪尔的,索引单子似乎是我需要的。到目前为止,我有以下代码:

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
我知道红色、
绿色
蓝色
的类型是不同的。但是这些错误对我来说没有意义,为什么编译器在
greenToBlue::IState Green-Blue()
时会期望
IState为绿色()
?在我看来,它希望所有类型都“匹配”第一个
案例
模式
redToGreen
。我如何解决这个问题来创建我的状态传递函数?“What is indexed monad?”帖子使用了GADT,因此我认为这可能会有所帮助,但我无法让该帖子中的示例发挥作用,我以前也没有使用过GADT,请阅读有关它们的文章

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


澄清:如果状态传递函数没有保留状态机结构,我希望编译器给出一个错误。假设我将状态机结构定义为:
Red
->
Green
->
Blue
->
Red
。。。但是,如果我不小心更改了
colorChange
函数,因此
红色
->
蓝色
,编译器应该发出错误,因为这违反了状态机结构,其中
绿色
必须跟随
红色

我建议保持简单

data Color = Red | Green | Blue

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

这就是我目前在我的项目中使用的,对于这个特定的例子来说,它非常有效。但是,如果我将
colorChange Red\u=Green
更改为
colorChange Red\u=Blue
,编译器不会警告我状态机的结构已更改,因为
Green
Blue
是同一类型。随着机器复杂性的增加,这就是我开始遇到的问题,我希望索引单子可以帮助解决这个问题。有点像一个附加的健全性检查,在我添加更多更改时,整个状态机结构仍然保持不变。@dopamane好的。那我就不明白这个问题了。您在问题中提出的类型不会强制执行类似的操作(即使在修复了
colorChange
类型错误之后)。如果您稍微讨论一下希望与状态机进行哪些交互,可能会有所帮助。运行它,我想?还有什么?什么东西在什么状态下是非法的,或者你想排除什么交互,或者。。。一般来说,你到底想做什么?继续“索引单元格”的直觉,我想你可能会考虑包括一些类似“代码> BIN::ista A B V ->(V -ististb B C V))->一个C V’< /代码>,不是吗?但是,
bind redToGreen(\\\->greenToBlue)
输入正确,并且从
Red
变为
Blue
。如果编译器也拒绝了这一点?@DanielWagner要回答下面的评论,主要的交互是
step::s->i->s'
,(摩尔版本)。我的目标是将状态机的定义(其结构,即状态彼此相邻)与状态转换的实现(
step
s)分开。如果
f::Red->Green
g::Green->Blue
h::Blue->Red
都是我的转换函数,那么
step
的定义方式应该是,如果它确实警告我,它永远不会变为
Red->Blue
。因此,
bind redToGreen(\\\->greenToBlue)
应该被拒绝。也许索引的monad不是我想要的。我应该发布工作代码(基本上是你回答的,虽然不是那么简洁),然后问我如何让编译器在
colorChange Red\u=Green
意外更改为
colorChange Red\u=Blue
时警告我。或者任何可能破坏结构的更改
红色->绿色->蓝色->红色
等等。但是你的代码已经打破了这种结构:
colorChange\ufalse
做了一些改变,例如转换
Red->Red
…对不起,我的示例很差。在本例中,我还将为每个状态进行标识转换,
idRed::Red->Red
idGreen::Green->Green
idBlue::Blue->Blue
。随着
f::Red->Green
g::Green->Blue
h::Blue->Red