-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathclosured_value.mdtlbl
More file actions
156 lines (141 loc) · 3.63 KB
/
closured_value.mdtlbl
File metadata and controls
156 lines (141 loc) · 3.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#**
* 这是在0.15.0添加的语法, 是一种特殊的值.
* 其用途可以在常量追值时对捕获表中进行求值
* 然后在展开前将捕获表中捕获的值加载
*
* 有两种捕获方式: 以take捕获 以const捕获
* 使用`A:B`表示以take捕获B到A, 相当于`take B=A;`
* 使用`&A:B`表示以const捕获B到A, 就不会take而是进行常量追值, 相当于`const B=A;`
*
* 有省略语法糖, 可以使用`A`表示`A:A`, 使用`&B`表示`&B:B`
*
* 语法起始处使用圆括号接方括号, 方括号内是捕获表, 方括号结束后接着一个值
*
* 在0.16.10添加了对label的捕获, 终于不用怕内层label和外层的同名了
* 使用方法在捕获的参数那里添加一个竖线, 然后在右边编写捕获的label
* 比如`([A B | :x](goto :x;))`
*#
const A = (a: print "makeA";);
const B = (b: print "makeB";);
const Clos = ([A &B](
# 因为闭包内部原理是将捕获值绑定到`..`, 所以你可以在其中重新修改它
# 但是因为在求值前就已经将内部注册量加载完毕了, 所以无法从加载完毕的值获取
# 例如你可以用`...B`获取而不能使用`B`
take ...B = makedB;
print "run" A B ...B;
));
print "inited";
const A = "errA"; # 因为闭包值在常量追值时就将值记录, 所以不会拿到这个
const B = "errB"; # 因为闭包值在常量追值时就将值记录, 所以不会拿到这个
print "do";
take Clos Clos;
#* >>>
print "makeA"
print "inited"
print "do"
print "run"
print a
print "makeB"
print b
print makedB
print "run"
print a
print makedB
print makedB
*#
# 在没有闭包语法时, 要实现闭包会比较麻烦
# 比如以如下办法实现
take Clos = ();
take Clos.A = (a: print "makeA";);
const Clos.B = (b: print "makeB";);
const Clos.V = (
print "run" ...A ...B;
);
print "inited";
print "do";
take Clos.V;
# 使用闭包捕获标记, 不会被内层跳转影响
const F = (
const Run = (const match @ {
F {
:x
print "unexpected";
take F[];
}
});
:x
print "expected";
take Run[([| :x](
goto :x;
))];
);
take F F; # 这里证明它不会被展开标签重命名破坏
#* >>>
print "expected"
print "unexpected"
jump 0 always 0 0
print "expected"
print "unexpected"
jump 3 always 0 0
*#
# 可以看到, 并没有跳到Run里面的:x, 而是闭包捕获到的
# 如果我们将闭包去掉的话, 会得到我们不希望看到的, 如下
#*
print "expected"
print "unexpected"
jump 1 always 0 0
print "expected"
print "unexpected"
jump 4 always 0 0
*#
# 在0.17.10版本添加了捕获环境参数的功能, 会在展开闭包前设置环境参数
# 在0.17.17版本认为之前的版本环境参数不设置旧形式是很坑的行为,
# 所以改成连着旧形式一起设置了, 也就是也会设置 _0 _1 等
const Builder = (
const X = 2;
const $.F = ([X @](
print X @;
));
);
const Clos = Builder[a b c]->F;
take Clos[d e f];
#* >>>
print 2
print a
print b
print c
*#
# 在0.19.5版本添加了懒闭包, 配合match编写,
# 用于避免闭包展开环境时破坏立即求值的传参环境卫生,
# 可以在match匹配后再展开闭包环境
const F = ([N:2]match @ {
R { print R; }
});
const N = 3;
F! (x:print N;);
#* >>>
print 3
print x
*#
# 如果不使用懒闭包的话, 例如
const F = ([N:2](match @ {
R { print R; }
}));
const N = 3;
F! (x:print N;);
#* >>>
print 2
print x
*#
# 可以看到, N 获取到了闭包内展开的环境N:2
# 也可以使用const match, 支持指定返回句柄, 例如
const F = ([N:2]res:match @ {
R { print R; }
});
const N = 3;
print F[(x:print N;)];
#* >>>
print 3
print x
print res
*#