如何从Coq中的归纳类型创建状态机?

时间:2019-06-20 21:47:19

标签: coq

如何在Coq中完全根据归纳类型创建正确的状态机(没有以无效方式构造状态机的方法)?

从类似的东西开始

Inductive Cmd :=
| Open
| Send
| Close.

Inductive SocketState :=
| Ready
| Opened
| Closed.

例如,从“就绪”到“已打开”的转换应该仅在“打开”命令之后发生。

2 个答案:

答案 0 :(得分:2)

来自the formal definition of deterministic finite state machine

  

确定性有限自动机M是5元组Q, Sigma, delta, q0, F,由以下组成:

     
      
  • 一组有限的状态Q
  •   
  • 一组有限的输入符号,称为字母Sigma
  •   
  • 转换函数deltaQ * Sigma -> Q
  •   
  • q0中的初始状态Q
  •   
  • 一组接受状态FQ的子集
  •   

您给了五分之二,即Q = SocketStateSigma = Cmd。假设您的应用程序具有隐式初始状态(可能为Ready)并且没有特定的“接受状态”,那么状态机唯一需要的就是转换函数

根据定义,过渡函数的类型为(SocketState * Cmd) -> SocketState,但咖喱版本SocketState -> Cmd -> SocketState同样强大。

如果状态机具有外部输入,请将其作为参数添加到函数中。如果您想要副作用,或与转换本身相关的某种输出,则可以使用SocketState -> Cmd -> (SomeOutput * SocketState)


编辑:过渡为一种关系(扩展为keep_learning's answer

如果要推理一系列 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]。

[1] https://github.com/mukeshtiwari/EncryptionSchulze/blob/master/code/Workingcode/EncryptionSchulze.v#L718-L740