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
。