如何使用索引monad作为FSM?

问题描述 投票:-1回答:1

我正在尝试创建一个简单的状态机,当输入有效时改变颜色状态: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?”发布后使用的GADTs,所以我想也许这会有所帮助,但是我无法在该帖子中得到示例,我之前没有使用过GADT,只是阅读了它们。

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

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

haskell monads state-machine
1个回答
2
投票

我建议保持简单。

data Color = Red | Green | Blue

colorChange :: Color -> Bool -> Color
colorChange s     False = s
colorChange Red   _     = Green
colorChange Green _     = Blue
colorChange Blue  _     = Red
© www.soinside.com 2019 - 2024. All rights reserved.