Skip to content
kata / a language workbench

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
    ]
  }
]
Download original counter.ks ↓

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.