我正在尝试创建一个简单的状态机,该状态机在输入有效时会更改颜色状态:
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?”该帖子使用了GADT,所以我认为这可能会有所帮助,但是我无法在该帖子中获得示例,我以前也没有使用GADT,只是阅读了有关它们的内容。
请注意,这对于调试和学习非常简单,我计划在我的FSM复杂性增加时使用它。
澄清:如果状态传递函数未保留状态机结构,我希望编译器给出错误信息。假设我将状态机结构定义为:Red
-> Green
-> Blue
-> Red
...但是如果我不小心更改了colorChange
函数因此Red
-> Blue
,编译器应发出错误,因为这违反了Green
必须遵循Red
的状态机结构。
答案 0 :(得分:2)
我建议保持简单。
data Color = Red | Green | Blue
colorChange :: Color -> Bool -> Color
colorChange s False = s
colorChange Red _ = Green
colorChange Green _ = Blue
colorChange Blue _ = Red