@@ -20,20 +20,20 @@ typed-fsm-zig 是一个利用 zig 类型系统加一些编程规范实现的一
20
20
21
21
它具有以下两点优势:
22
22
23
- 1 . 类型安全,极大方便代码的编写,修改和重构
24
- 手写状态机在实际代码中有很大的心智负担,对于它的修改和重构更是如噩梦一样。
23
+ 1 . 类型安全,极大方便代码的编写,修改和重构
24
+ 手写状态机在实际代码中有很大的心智负担,对于它的修改和重构更是如噩梦一样。
25
25
26
- typed-fsm-zig 在类型上跟踪状态机的变化,使消息的定义,产生,处理都和状态相关联,从而让类型系统帮我们检查这个过程中是否存在状态错误。
26
+ typed-fsm-zig 在类型上跟踪状态机的变化,使消息的定义,产生,处理都和状态相关联,从而让类型系统帮我们检查这个过程中是否存在状态错误。
27
27
28
- 在编写,修改和重构的时候,任何状态的错误都会产生编译错误,而这些编译错误能帮助我们快速找到问题,解决问题。
28
+ 在编写,修改和重构的时候,任何状态的错误都会产生编译错误,而这些编译错误能帮助我们快速找到问题,解决问题。
29
29
30
- > PS:推荐在 zls 中打开保存时检查,这样你几乎能得到一个交互式的状态机开发环境。
30
+ > PS:推荐在 zls 中打开保存时检查,这样你几乎能得到一个交互式的状态机开发环境。
31
31
32
- 2 . 简单高效,无任何代码生成,能方便与现有逻辑整合
32
+ 2 . 简单高效,无任何代码生成,能方便与现有逻辑整合
33
33
34
- typed-fsm-zig 是一种编程的思想,掌握这种思想就能方便的使用它。
34
+ typed-fsm-zig 是一种编程的思想,掌握这种思想就能方便的使用它。
35
35
36
- 在实际的使用中没有任何的代码生成,除了一处隐式的约束要求之外,没有任何其它的控制,开发者完全掌握状态机,因此你可以方便的将它和你现有的代码结合起来。
36
+ 在实际的使用中没有任何的代码生成,除了一处隐式的约束要求之外,没有任何其它的控制,开发者完全掌握状态机,因此你可以方便的将它和你现有的代码结合起来。
37
37
38
38
# 2. 例子:修改 ATM 状态机的状态
39
39
@@ -76,7 +76,7 @@ ATM 代表自动取款机,因此它的代码的逻辑就是模拟自动取款
76
76
77
77
实际的消息 Update 定义代码如下
78
78
79
- ``` c
79
+ ``` zig
80
80
pub fn changePinMsg(end: AtmSt) type {
81
81
return union(enum) {
82
82
Update: struct { v: [4]u8, wit: WitFn(end, .ready) = .{} },
@@ -124,8 +124,8 @@ referenced by:
124
124
125
125
修改之后的代码如下:
126
126
127
- ``` c
128
- @call(.always_tail, cardInsertedHandler, .{ val.wit, ist });
127
+ ` ` ` zig
128
+ @call(.always_tail, cardInsertedHandler, .{ val.wit, ist });
129
129
` ` `
130
130
131
131
在这里类型系统精确的告诉了我们需要修改的地方,以及原因。修改完成后程序即能正确运行。
@@ -313,7 +313,7 @@ fn s2Handler(val: Witness(Exmaple, .exit, .s2), ref: *i32) void {
313
313
314
314
当 end == start 时表示当前处于终止状态,因此 Witness 只能使用 terminal 函数,当 end ! = start 时表示当前不处于终止状态,应该继续从外部获取消息,因此 Witness 只能使用 getMsg 函数。
315
315
316
- ` ` ` c
316
+ ` ` ` zig
317
317
pub fn Witness(T: type, end: T, start: T) type {
318
318
return struct {
319
319
pub fn getMsg(self: @This ()) @TypeOf(start.STM(end).getMsg) {
@@ -337,7 +337,7 @@ pub fn Witness(T: type, end: T, start: T) type {
337
337
338
338
实际代码中会将消息集合整合在 enum 的内部,使用特殊的命名规范将状态与消息集合对应。目前的隐式规范是在状态后面加上 Msg。
339
339
340
- ` ` ` c
340
+ ` ` ` zig
341
341
const Exmaple = enum {
342
342
exit,
343
343
s1,
@@ -359,7 +359,7 @@ const Exmaple = enum {
359
359
360
360
接下来是消息的定义和产生,
361
361
362
- ` ` ` c
362
+ ` ` ` zig
363
363
// exit 状态下没有任何消息
364
364
pub fn exitMsg(_: Exmaple) void {
365
365
return {};
@@ -406,7 +406,7 @@ pub fn s2Msg(end: Exmaple) type {
406
406
407
407
这些对于代码的编写,修改,重构都有巨大的帮助。
408
408
409
- ` ` ` c
409
+ ` ` ` zig
410
410
fn s1Handler(val: Witness(Exmaple, .exit, .s1), ref: * i32) void {
411
411
std.debug.print(" val: {d}\n" , .{ref.* });
412
412
switch (val.getMsg()(ref)) {
@@ -432,19 +432,19 @@ fn s2Handler(val: Witness(Exmaple, .exit, .s2), ref: *i32) void {
432
432
433
433
# 4. typed-fsm-zig 需要哪些编程规范
434
434
435
- # # 1. 状态和消息集合之间需要满足的隐式命名规范
435
+ 1. 状态和消息集合之间需要满足的隐式命名规范
436
436
437
- 以 ATM 为例:
437
+ 以 ATM 为例:
438
438
439
- exit -- exitMsg
439
+ exit -- exitMsg
440
440
441
- ready -- readyMsg
441
+ ready -- readyMsg
442
442
443
- cardInserted -- cardInsertedMsg
443
+ cardInserted -- cardInsertedMsg
444
444
445
- session -- sessionMsg
445
+ session -- sessionMsg
446
446
447
- ` ` ` c
447
+ ` ` ` zig
448
448
449
449
const AtmSt = enum {
450
450
exit,
@@ -492,15 +492,15 @@ const AtmSt = enum {
492
492
};
493
493
` ` `
494
494
495
- # # 2. 除了 exit 状态外,其它消息需要包含 genMsg 函数用于产生消息,任何消息都必须带有 Witness
495
+ 2. 除了 exit 状态外,其它消息需要包含 genMsg 函数用于产生消息,任何消息都必须带有 Witness
496
496
497
- # # 3. 状态机都需要定义退出状态,尽管你可能永远也不会退出状态机,但退出状态作用于类型上,是不可缺少的
497
+ 3. 状态机都需要定义退出状态,尽管你可能永远也不会退出状态机,但退出状态作用于类型上,是不可缺少的
498
498
499
- # # 4. 在互相调用其它 handler 的时候使用尾递归的语法,并且在必须在语句块最后处理消息附带的 Witness
499
+ 4. 在互相调用其它 handler 的时候使用尾递归的语法,并且在必须在语句块最后处理消息附带的 Witness
500
500
501
501
由于 zig 的实现缺少 Mcbride Indexed Monad 语义的支持,因此类型系统不能阻止你进行下面的操作:
502
502
503
- ` ` ` c
503
+ ` ` ` zig
504
504
505
505
// 使用上面 Example 的处理函数 s1Handler, 将它修改成下面的样子。
506
506
// 这里的 s1Handler 不应该被多次调用,在 haskell 版本的 typed-fsm 中,类型系统能检查出这里类型错误,但是在 zig 实现中无法做到,
@@ -525,7 +525,7 @@ fn s2Handler(val: Witness(Exmaple, .exit, .s2), ref: *i32) void {
525
525
526
526
在实际的 ATM 例子中他们的调用方式是:
527
527
528
- ` ` ` c
528
+ ` ` ` zig
529
529
pub fn readyHandler(comptime w: AtmSt.EWitness(.ready), ist: * InternalState) void {
530
530
switch (w.getMsg()()) {
531
531
.ExitAtm => | witness| {
0 commit comments