Closure counter
A factory returns a function that increments before returning its current value. Interleaved calls make the independence of two counters visible.
func make_counter(start: Int, step: Int): Func { let value = start ret func(): Int { value = value + step ret value }}let next = make_counter(0, 1)let tens = make_counter(0, 10)print("next: {next()}")print("next: {next()}")print("tens: {tens()}")print("next: {next()}")print("tens: {tens()}")Original example: checked output and syntax tree
These records belong to the original downloadable program. Run the editor above to see results for your changes.
next: 1 next: 2 tens: 10 next: 3 tens: 20
Original AST
[
{
"node": {
"FuncDef": {
"name": {
"node": "make_counter",
"span": [
5,
17
]
},
"params": [
{
"name": {
"node": "start",
"span": [
18,
23
]
},
"type_ann": {
"node": {
"Name": "Int"
},
"span": [
25,
28
]
}
},
{
"name": {
"node": "step",
"span": [
30,
34
]
},
"type_ann": {
"node": {
"Name": "Int"
},
"span": [
36,
39
]
}
}
],
"ret_type": {
"node": {
"Name": "Func"
},
"span": [
42,
46
]
},
"body": [
{
"node": {
"Let": {
"pattern": {
"node": {
"Binding": {
"node": "value",
"span": [
57,
62
]
}
},
"span": [
57,
62
]
},
"type_ann": null,
"value": {
"node": {
"Name": "start"
},
"span": [
65,
70
]
}
}
},
"span": [
53,
70
]
},
{
"node": {
"Ret": {
"keyword": [
75,
78
],
"value": {
"node": {
"FuncExpr": {
"params": [],
"ret_type": {
"node": {
"Name": "Int"
},
"span": [
87,
90
]
},
"body": [
{
"node": {
"Assign": {
"target": {
"Name": "value"
},
"value": {
"node": {
"BinOp": {
"op": "Add",
"left": {
"node": {
"Name": "value"
},
"span": [
109,
114
]
},
"right": {
"node": {
"Name": "step"
},
"span": [
117,
121
]
}
}
},
"span": [
109,
121
]
}
}
},
"span": [
101,
121
]
},
{
"node": {
"Ret": {
"keyword": [
130,
133
],
"value": {
"node": {
"Name": "value"
},
"span": [
134,
139
]
}
}
},
"span": [
130,
139
]
}
]
}
},
"span": [
79,
145
]
}
}
},
"span": [
75,
145
]
}
]
}
},
"span": [
0,
147
]
},
{
"node": {
"Let": {
"pattern": {
"node": {
"Binding": {
"node": "next",
"span": [
152,
156
]
}
},
"span": [
152,
156
]
},
"type_ann": null,
"value": {
"node": {
"Call": {
"callee": {
"node": {
"Name": "make_counter"
},
"span": [
159,
171
]
},
"args": [
{
"node": {
"Int": "0"
},
"span": [
172,
173
]
},
{
"node": {
"Int": "1"
},
"span": [
175,
176
]
}
],
"args_span": [
171,
177
]
}
},
"span": [
159,
177
]
}
}
},
"span": [
148,
177
]
},
{
"node": {
"Let": {
"pattern": {
"node": {
"Binding": {
"node": "tens",
"span": [
182,
186
]
}
},
"span": [
182,
186
]
},
"type_ann": null,
"value": {
"node": {
"Call": {
"callee": {
"node": {
"Name": "make_counter"
},
"span": [
189,
201
]
},
"args": [
{
"node": {
"Int": "0"
},
"span": [
202,
203
]
},
{
"node": {
"Int": "10"
},
"span": [
205,
207
]
}
],
"args_span": [
201,
208
]
}
},
"span": [
189,
208
]
}
}
},
"span": [
178,
208
]
},
{
"node": {
"Expr": {
"node": {
"Call": {
"callee": {
"node": {
"Name": "print"
},
"span": [
209,
214
]
},
"args": [
{
"node": {
"Interp": {
"parts": [
{
"Lit": "next: "
},
{
"Expr": {
"node": {
"Call": {
"callee": {
"node": {
"Name": "next"
},
"span": [
223,
227
]
},
"args": [],
"args_span": [
227,
229
]
}
},
"span": [
223,
229
]
}
}
]
}
},
"span": [
215,
231
]
}
],
"args_span": [
214,
232
]
}
},
"span": [
209,
232
]
}
},
"span": [
209,
232
]
},
{
"node": {
"Expr": {
"node": {
"Call": {
"callee": {
"node": {
"Name": "print"
},
"span": [
233,
238
]
},
"args": [
{
"node": {
"Interp": {
"parts": [
{
"Lit": "next: "
},
{
"Expr": {
"node": {
"Call": {
"callee": {
"node": {
"Name": "next"
},
"span": [
247,
251
]
},
"args": [],
"args_span": [
251,
253
]
}
},
"span": [
247,
253
]
}
}
]
}
},
"span": [
239,
255
]
}
],
"args_span": [
238,
256
]
}
},
"span": [
233,
256
]
}
},
"span": [
233,
256
]
},
{
"node": {
"Expr": {
"node": {
"Call": {
"callee": {
"node": {
"Name": "print"
},
"span": [
257,
262
]
},
"args": [
{
"node": {
"Interp": {
"parts": [
{
"Lit": "tens: "
},
{
"Expr": {
"node": {
"Call": {
"callee": {
"node": {
"Name": "tens"
},
"span": [
271,
275
]
},
"args": [],
"args_span": [
275,
277
]
}
},
"span": [
271,
277
]
}
}
]
}
},
"span": [
263,
279
]
}
],
"args_span": [
262,
280
]
}
},
"span": [
257,
280
]
}
},
"span": [
257,
280
]
},
{
"node": {
"Expr": {
"node": {
"Call": {
"callee": {
"node": {
"Name": "print"
},
"span": [
281,
286
]
},
"args": [
{
"node": {
"Interp": {
"parts": [
{
"Lit": "next: "
},
{
"Expr": {
"node": {
"Call": {
"callee": {
"node": {
"Name": "next"
},
"span": [
295,
299
]
},
"args": [],
"args_span": [
299,
301
]
}
},
"span": [
295,
301
]
}
}
]
}
},
"span": [
287,
303
]
}
],
"args_span": [
286,
304
]
}
},
"span": [
281,
304
]
}
},
"span": [
281,
304
]
},
{
"node": {
"Expr": {
"node": {
"Call": {
"callee": {
"node": {
"Name": "print"
},
"span": [
305,
310
]
},
"args": [
{
"node": {
"Interp": {
"parts": [
{
"Lit": "tens: "
},
{
"Expr": {
"node": {
"Call": {
"callee": {
"node": {
"Name": "tens"
},
"span": [
319,
323
]
},
"args": [],
"args_span": [
323,
325
]
}
},
"span": [
319,
325
]
}
}
]
}
},
"span": [
311,
327
]
}
],
"args_span": [
310,
328
]
}
},
"span": [
305,
328
]
}
},
"span": [
305,
328
]
}
]What survives the factory call
Section titled “What survives the factory call”Each call to make_counter creates a new value binding. The returned function captures that binding along with step. Assignment updates the existing binding, so the next call sees the previous call’s result.
next and tens come from different factory calls and therefore have different scalar state. Copying one returned function into another binding would not create another factory call or reset its captured state.
The counter tutorial starts with a top-level captured binding and then moves it into a factory. See functions and closures for annotations, anonymous functions, and shadowing.