Skip to content
kata / a language workbench

Custom iterator

A for loop needs an iterable that creates an iterator, and an iterator that yields optional values. This countdown implements both pieces in KataScript.

kind Countdown { start: Int }
kind CountdownIter { current: Int }
impl Countdown as ToIter[Int] {
func to_iter(self): CountdownIter {
ret CountdownIter { current: self.start }
}
}
impl CountdownIter as Iter[Int] {
func next(self): Opt[Int] {
if self.current <= 0 { ret Opt[Int].Non }
let value = self.current
self.current = self.current - 1
ret Opt[Int].Val(value)
}
}
let launch = Countdown { start: 3 }
for n in launch { print(n) }
print("go")
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.

3
2
1
go
Original AST
[
  {
    "node": {
      "KindDef": {
        "name": {
          "node": "Countdown",
          "span": [
            5,
            14
          ]
        },
        "type_params": [],
        "fields": [
          {
            "name": {
              "node": "start",
              "span": [
                17,
                22
              ]
            },
            "type_ann": {
              "node": {
                "Name": "Int"
              },
              "span": [
                24,
                27
              ]
            }
          }
        ]
      }
    },
    "span": [
      0,
      29
    ]
  },
  {
    "node": {
      "KindDef": {
        "name": {
          "node": "CountdownIter",
          "span": [
            35,
            48
          ]
        },
        "type_params": [],
        "fields": [
          {
            "name": {
              "node": "current",
              "span": [
                51,
                58
              ]
            },
            "type_ann": {
              "node": {
                "Name": "Int"
              },
              "span": [
                60,
                63
              ]
            }
          }
        ]
      }
    },
    "span": [
      30,
      65
    ]
  },
  {
    "node": {
      "Impl": {
        "target": {
          "node": {
            "Concrete": "Countdown"
          },
          "span": [
            71,
            80
          ]
        },
        "as_type": {
          "node": {
            "Item": {
              "object": {
                "node": {
                  "Name": "ToIter"
                },
                "span": [
                  84,
                  90
                ]
              },
              "args": [
                {
                  "node": {
                    "Name": "Int"
                  },
                  "span": [
                    91,
                    94
                  ]
                }
              ]
            }
          },
          "span": [
            84,
            95
          ]
        },
        "methods": [
          {
            "node": {
              "name": {
                "node": "to_iter",
                "span": [
                  107,
                  114
                ]
              },
              "params": [
                {
                  "name": {
                    "node": "self",
                    "span": [
                      115,
                      119
                    ]
                  },
                  "type_ann": null
                }
              ],
              "ret_type": {
                "node": {
                  "Name": "CountdownIter"
                },
                "span": [
                  122,
                  135
                ]
              },
              "body": [
                {
                  "node": {
                    "Ret": {
                      "keyword": [
                        146,
                        149
                      ],
                      "value": {
                        "node": {
                          "Construct": {
                            "type_expr": {
                              "node": {
                                "Name": "CountdownIter"
                              },
                              "span": [
                                150,
                                163
                              ]
                            },
                            "fields": [
                              [
                                "current",
                                {
                                  "node": {
                                    "Attr": {
                                      "object": {
                                        "node": {
                                          "Name": "self"
                                        },
                                        "span": [
                                          175,
                                          179
                                        ]
                                      },
                                      "name": "start",
                                      "name_span": [
                                        180,
                                        185
                                      ]
                                    }
                                  },
                                  "span": [
                                    175,
                                    185
                                  ]
                                }
                              ]
                            ],
                            "open_brace": [
                              164,
                              165
                            ]
                          }
                        },
                        "span": [
                          150,
                          187
                        ]
                      }
                    }
                  },
                  "span": [
                    146,
                    187
                  ]
                }
              ]
            },
            "span": [
              102,
              193
            ]
          }
        ]
      }
    },
    "span": [
      66,
      195
    ]
  },
  {
    "node": {
      "Impl": {
        "target": {
          "node": {
            "Concrete": "CountdownIter"
          },
          "span": [
            201,
            214
          ]
        },
        "as_type": {
          "node": {
            "Item": {
              "object": {
                "node": {
                  "Name": "Iter"
                },
                "span": [
                  218,
                  222
                ]
              },
              "args": [
                {
                  "node": {
                    "Name": "Int"
                  },
                  "span": [
                    223,
                    226
                  ]
                }
              ]
            }
          },
          "span": [
            218,
            227
          ]
        },
        "methods": [
          {
            "node": {
              "name": {
                "node": "next",
                "span": [
                  239,
                  243
                ]
              },
              "params": [
                {
                  "name": {
                    "node": "self",
                    "span": [
                      244,
                      248
                    ]
                  },
                  "type_ann": null
                }
              ],
              "ret_type": {
                "node": {
                  "Item": {
                    "object": {
                      "node": {
                        "Name": "Opt"
                      },
                      "span": [
                        251,
                        254
                      ]
                    },
                    "args": [
                      {
                        "node": {
                          "Name": "Int"
                        },
                        "span": [
                          255,
                          258
                        ]
                      }
                    ]
                  }
                },
                "span": [
                  251,
                  259
                ]
              },
              "body": [
                {
                  "node": {
                    "Expr": {
                      "node": {
                        "If": {
                          "cond": {
                            "node": {
                              "BinOp": {
                                "op": "Le",
                                "left": {
                                  "node": {
                                    "Attr": {
                                      "object": {
                                        "node": {
                                          "Name": "self"
                                        },
                                        "span": [
                                          273,
                                          277
                                        ]
                                      },
                                      "name": "current",
                                      "name_span": [
                                        278,
                                        285
                                      ]
                                    }
                                  },
                                  "span": [
                                    273,
                                    285
                                  ]
                                },
                                "right": {
                                  "node": {
                                    "Int": "0"
                                  },
                                  "span": [
                                    289,
                                    290
                                  ]
                                }
                              }
                            },
                            "span": [
                              273,
                              290
                            ]
                          },
                          "then_body": [
                            {
                              "node": {
                                "Ret": {
                                  "keyword": [
                                    293,
                                    296
                                  ],
                                  "value": {
                                    "node": {
                                      "Attr": {
                                        "object": {
                                          "node": {
                                            "Item": {
                                              "object": {
                                                "node": {
                                                  "Name": "Opt"
                                                },
                                                "span": [
                                                  297,
                                                  300
                                                ]
                                              },
                                              "args": [
                                                {
                                                  "node": {
                                                    "Name": "Int"
                                                  },
                                                  "span": [
                                                    301,
                                                    304
                                                  ]
                                                }
                                              ]
                                            }
                                          },
                                          "span": [
                                            297,
                                            305
                                          ]
                                        },
                                        "name": "Non",
                                        "name_span": [
                                          306,
                                          309
                                        ]
                                      }
                                    },
                                    "span": [
                                      297,
                                      309
                                    ]
                                  }
                                }
                              },
                              "span": [
                                293,
                                309
                              ]
                            }
                          ],
                          "else_body": null
                        }
                      },
                      "span": [
                        270,
                        311
                      ]
                    }
                  },
                  "span": [
                    270,
                    311
                  ]
                },
                {
                  "node": {
                    "Let": {
                      "pattern": {
                        "node": {
                          "Binding": {
                            "node": "value",
                            "span": [
                              324,
                              329
                            ]
                          }
                        },
                        "span": [
                          324,
                          329
                        ]
                      },
                      "type_ann": null,
                      "value": {
                        "node": {
                          "Attr": {
                            "object": {
                              "node": {
                                "Name": "self"
                              },
                              "span": [
                                332,
                                336
                              ]
                            },
                            "name": "current",
                            "name_span": [
                              337,
                              344
                            ]
                          }
                        },
                        "span": [
                          332,
                          344
                        ]
                      }
                    }
                  },
                  "span": [
                    320,
                    344
                  ]
                },
                {
                  "node": {
                    "Assign": {
                      "target": {
                        "Attr": {
                          "object": {
                            "node": {
                              "Name": "self"
                            },
                            "span": [
                              353,
                              357
                            ]
                          },
                          "attr": "current"
                        }
                      },
                      "value": {
                        "node": {
                          "BinOp": {
                            "op": "Sub",
                            "left": {
                              "node": {
                                "Attr": {
                                  "object": {
                                    "node": {
                                      "Name": "self"
                                    },
                                    "span": [
                                      368,
                                      372
                                    ]
                                  },
                                  "name": "current",
                                  "name_span": [
                                    373,
                                    380
                                  ]
                                }
                              },
                              "span": [
                                368,
                                380
                              ]
                            },
                            "right": {
                              "node": {
                                "Int": "1"
                              },
                              "span": [
                                383,
                                384
                              ]
                            }
                          }
                        },
                        "span": [
                          368,
                          384
                        ]
                      }
                    }
                  },
                  "span": [
                    353,
                    384
                  ]
                },
                {
                  "node": {
                    "Ret": {
                      "keyword": [
                        393,
                        396
                      ],
                      "value": {
                        "node": {
                          "Call": {
                            "callee": {
                              "node": {
                                "Attr": {
                                  "object": {
                                    "node": {
                                      "Item": {
                                        "object": {
                                          "node": {
                                            "Name": "Opt"
                                          },
                                          "span": [
                                            397,
                                            400
                                          ]
                                        },
                                        "args": [
                                          {
                                            "node": {
                                              "Name": "Int"
                                            },
                                            "span": [
                                              401,
                                              404
                                            ]
                                          }
                                        ]
                                      }
                                    },
                                    "span": [
                                      397,
                                      405
                                    ]
                                  },
                                  "name": "Val",
                                  "name_span": [
                                    406,
                                    409
                                  ]
                                }
                              },
                              "span": [
                                397,
                                409
                              ]
                            },
                            "args": [
                              {
                                "node": {
                                  "Name": "value"
                                },
                                "span": [
                                  410,
                                  415
                                ]
                              }
                            ],
                            "args_span": [
                              409,
                              416
                            ]
                          }
                        },
                        "span": [
                          397,
                          416
                        ]
                      }
                    }
                  },
                  "span": [
                    393,
                    416
                  ]
                }
              ]
            },
            "span": [
              234,
              422
            ]
          }
        ]
      }
    },
    "span": [
      196,
      424
    ]
  },
  {
    "node": {
      "Let": {
        "pattern": {
          "node": {
            "Binding": {
              "node": "launch",
              "span": [
                429,
                435
              ]
            }
          },
          "span": [
            429,
            435
          ]
        },
        "type_ann": null,
        "value": {
          "node": {
            "Construct": {
              "type_expr": {
                "node": {
                  "Name": "Countdown"
                },
                "span": [
                  438,
                  447
                ]
              },
              "fields": [
                [
                  "start",
                  {
                    "node": {
                      "Int": "3"
                    },
                    "span": [
                      457,
                      458
                    ]
                  }
                ]
              ],
              "open_brace": [
                448,
                449
              ]
            }
          },
          "span": [
            438,
            460
          ]
        }
      }
    },
    "span": [
      425,
      460
    ]
  },
  {
    "node": {
      "Expr": {
        "node": {
          "For": {
            "pattern": {
              "node": {
                "Binding": {
                  "node": "n",
                  "span": [
                    465,
                    466
                  ]
                }
              },
              "span": [
                465,
                466
              ]
            },
            "iter_expr": {
              "node": {
                "Name": "launch"
              },
              "span": [
                470,
                476
              ]
            },
            "body": [
              {
                "node": {
                  "Expr": {
                    "node": {
                      "Call": {
                        "callee": {
                          "node": {
                            "Name": "print"
                          },
                          "span": [
                            479,
                            484
                          ]
                        },
                        "args": [
                          {
                            "node": {
                              "Name": "n"
                            },
                            "span": [
                              485,
                              486
                            ]
                          }
                        ],
                        "args_span": [
                          484,
                          487
                        ]
                      }
                    },
                    "span": [
                      479,
                      487
                    ]
                  }
                },
                "span": [
                  479,
                  487
                ]
              }
            ]
          }
        },
        "span": [
          461,
          489
        ]
      }
    },
    "span": [
      461,
      489
    ]
  },
  {
    "node": {
      "Expr": {
        "node": {
          "Call": {
            "callee": {
              "node": {
                "Name": "print"
              },
              "span": [
                490,
                495
              ]
            },
            "args": [
              {
                "node": {
                  "Str": "go"
                },
                "span": [
                  496,
                  500
                ]
              }
            ],
            "args_span": [
              495,
              501
            ]
          }
        },
        "span": [
          490,
          501
        ]
      }
    },
    "span": [
      490,
      501
    ]
  }
]
Download original custom-iterator.ks ↓

Countdown stores the starting number. Its to_iter() method creates fresh traversal state, so another loop can start the countdown again.

CountdownIter stores the current number. Each next() call returns Opt[Int].Val(value) and moves the current number downward. Once it reaches zero, it returns Opt[Int].Non.

The value is saved before mutation, so starting at three yields 3, 2, 1. Starting at zero or a negative number yields no values.

The impl ... as ToIter[Int] and impl ... as Iter[Int] declarations use interfaces from the prelude. The loop calls to_iter() once and keeps the updated iterator between calls to next().

There is no range literal or built-in range() required here. The iteration mechanism is ordinary methods over a small state value.

Read methods and interfaces for implementation syntax and control flow for loop patterns.