我正在尝试创建一个简单的状态机,当输入有效时改变颜色状态: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
据我所知,Red
,Green
和Blue
的类型不同。但这些错误对我没有意义,为什么编译器会在IState a Green ()
时期待greenToBlue :: IState Green Blue ()
?在我看来,它期待所有类型“匹配”第一个case
模式redToGreen
。我如何解决这个问题来创建我的状态转移功能? “什么是索引monad?”发布后使用的GADTs,所以我想也许这会有所帮助,但是我无法在该帖子中得到示例,我之前没有使用过GADT,只是阅读了它们。
请注意,这对于调试和学习来说非常简单,我计划在我的FSM复杂性增加时使用它。
澄清:如果状态传递函数不保留状态机结构,我希望编译器给出错误。假设我将状态机结构定义为:Red
- > Green
- > Blue
- > Red
...但如果我不小心改变了我的colorChange
函数所以Red
- > Blue
,编译器应该发出错误,因为这违反了状态机结构Green
必须关注Red
。
我建议保持简单。
data Color = Red | Green | Blue
colorChange :: Color -> Bool -> Color
colorChange s False = s
colorChange Red _ = Green
colorChange Green _ = Blue
colorChange Blue _ = Red