如何在Coq中完全根据归纳类型创建正确的状态机(没有以无效方式构造状态机的方法)?
从类似的东西开始
Inductive Cmd :=
| Open
| Send
| Close.
Inductive SocketState :=
| Ready
| Opened
| Closed.
例如,从“就绪”到“已打开”的转换应该仅在“打开”命令之后发生。
答案 0 :(得分:2)
来自the formal definition of deterministic finite state machine:
确定性有限自动机
M
是5元组Q, Sigma, delta, q0, F
,由以下组成:
- 一组有限的状态
Q
- 一组有限的输入符号,称为字母
Sigma
- 转换函数
delta
:Q * Sigma -> Q
q0
中的初始状态Q
- 一组接受状态
F
是Q
的子集
您给了五分之二,即Q = SocketState
和Sigma = Cmd
。假设您的应用程序具有隐式初始状态(可能为Ready
)并且没有特定的“接受状态”,那么状态机唯一需要的就是转换函数。
根据定义,过渡函数的类型为(SocketState * Cmd) -> SocketState
,但咖喱版本SocketState -> Cmd -> SocketState
同样强大。
如果状态机具有外部输入,请将其作为参数添加到函数中。如果您想要副作用,或与转换本身相关的某种输出,则可以使用SocketState -> Cmd -> (SomeOutput * SocketState)
。
如果要推理一系列 valid 命令和转换,则可能需要将其编码为三元关系。
首先,让我们定义有效转换的构成。
Previous state -> (Command) -> Next state
-----------------------------------------
Ready -> (Open) -> Opened
Opened -> (Send) -> Opened
Opened -> (Close) -> Closed
然后,将其编码为三元关系。以下仅是示例,类似于编程语言模型中的Hoare triples。
Inductive Transition : SocketState -> Cmd -> SocketState -> Prop :=
| t_open : Transition Ready Open Opened
| t_send : Transition Opened Send Opened
| t_close : Transition Opened Close Closed.
以上讨论了单个过渡。过渡的系列怎么样?我们可以定义反射自传递闭包,并使用list
个命令(这非常类似于Hoare三元组,在某种意义上,它们都定义了一个前提条件,一系列步骤和一个后置条件):
From Coq Require Import List.
Import ListNotations.
Inductive TransitionRTC : SocketState -> list Cmd -> SocketState -> Prop :=
| t_rtc_refl : forall s : SocketState, TransitionRTC s [] s
| t_rtc_trans1 : forall s1 c s2 clist s3,
Transition s1 c s2 ->
TransitionRTC s2 clist s3 ->
TransitionRTC s1 (c :: clist) s3.
RTC关系的函数类似物是(与Haskell的fold_left
或Ocaml的foldl
相比,Coq中的fold_left
交换了最后两个参数):
Axiom transition : SocketState -> Cmd -> SocketState.
Definition multistep_transition (state0 : SocketState) (clist : list Cmd) :=
fold_left transition clist state0.
答案 1 :(得分:2)
您可以将规则(转换函数)编码为归纳数据类型。
Inductive Valid_transition : SocketState -> SocketState -> Type :=
| copen x : x = Open -> Valid_transition Ready Opened (* Input command Open *)
| cready x y : x = Send -> Valid_transition y Opened ->
Valid_transition Opened Opened (* Send command would not change the
status of port *)
| cclose x y : x = Close -> Valid_transition y Opened ->
Valid_transition Opened Closed. (* Close command would close it *)
Check (cready Send _ eq_refl (copen Open eq_refl)).
从“就绪”转换为“打开”的唯一方法是使用“打开”命令的第一个构造函数。第二个构造函数指出,如果您的命令是Send,并且您处于Opened状态,那么您将继续保持此状态。最后,第三个构造函数在收到“关闭”命令后关闭打开的端口。我已经编码了一个与您类似的转换函数(将其计为状态机),请随时查看[1]。