{"id":32197039,"url":"https://github.com/doublep/iter2","last_synced_at":"2026-02-21T00:02:26.775Z","repository":{"id":26371743,"uuid":"108428720","full_name":"doublep/iter2","owner":"doublep","description":"Reimplementation of Elisp generators","archived":false,"fork":false,"pushed_at":"2025-02-09T15:17:24.000Z","size":182,"stargazers_count":10,"open_issues_count":3,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-08T06:42:12.871Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Emacs Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/doublep.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-26T15:16:05.000Z","updated_at":"2025-04-10T06:05:55.000Z","dependencies_parsed_at":"2024-06-19T15:26:59.252Z","dependency_job_id":"3812c8b0-655e-4997-9153-89cfb4d5a84e","html_url":"https://github.com/doublep/iter2","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/doublep/iter2","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doublep%2Fiter2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doublep%2Fiter2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doublep%2Fiter2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doublep%2Fiter2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/doublep","download_url":"https://codeload.github.com/doublep/iter2/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doublep%2Fiter2/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29668633,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T23:24:07.480Z","status":"ssl_error","status_checked_at":"2026-02-20T23:24:06.202Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-22T02:49:02.511Z","updated_at":"2026-02-21T00:02:26.769Z","avatar_url":"https://github.com/doublep.png","language":"Emacs Lisp","funding_links":[],"categories":[],"sub_categories":[],"readme":":toc: macro\n:toc-title: Table of contents\n:source-language: lisp\nifndef::env-github[:icons: font]\nifdef::env-github[]\n:warning-caption: :warning:\n:caution-caption: :fire:\n:important-caption: :exclamation:\n:note-caption: :paperclip:\n:tip-caption: :bulb:\nendif::[]\n:uri-stable: http://stable.melpa.org/#/iter2\n:uri-unstable: http://melpa.org/#/iter2\n:uri-eldev: https://github.com/doublep/eldev\n:uri-wp-generators: https://en.wikipedia.org/wiki/Generator_(computer_programming)\n:uri-emacs-bug-26073: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=26073\n\n\n= iter2\n\nimage:https://img.shields.io/badge/license-GPL_3-green.svg[License: GPL 3, link=http://www.gnu.org/licenses/gpl-3.0.txt]\nimage:http://stable.melpa.org/packages/iter2-badge.svg[MELPA Stable, link=http://stable.melpa.org/#/iter2]\nimage:https://github.com/doublep/iter2/workflows/CI/badge.svg[CI, link=https://github.com/doublep/iter2/actions?query=workflow%3ACI]\n// Currently out of date, doesn't get updated from GitHub workflows.\n// [![Coverage Status](https://coveralls.io/repos/github/doublep/iter2/badge.svg)](https://coveralls.io/github/doublep/iter2)\n\n\n`iter2` is a fully compatible reimplementation of built-in `generator`\npackage.  It provides `iter2-defun` and `iter2-lambda` forms that can\nbe used in place of `iter-defun` and `iter-lambda`.  Form `iter2-next`\nis a replacement for `iter-next` (\u003c\u003citer2-next-explained,see below\u003e\u003e),\nthough `iter-next` will work too.\n\nOther functions and macros (`iter-yield`, `iter-yield-from`, `iter-do`\nand `iter-close`) are intentionally not duplicated: just use the ones\nfrom the original package.\n\nAdvantages:\n\n* Support for `save-excursion` and similar special forms and also\n  macro `save-match-data` (see \u003c\u003c#save-x,detailed description\n  below\u003e\u003e).\n* Generator functions can be debugged with Edebug.\n* Much faster conversion of complex generator functions.\n* Generally faster resulting functions.\n* Considerably smaller generated code, especially for complex\n  functions.\n* More readable resulting functions and backtraces.\n* Built-in tracing support for the produced generator functions.\n\nDisadvantages:\n\n* Because `iter2` conversion is heavily optimized, it is not as\n  generic as in original `generator` package and is, therefore, more\n  prone to bugs.\n\n\n== Installation\n\n`iter2` is available from MELPA (I recommend using the\n{uri-stable}[stable] version).  Assuming your `package-archives` lists\nMELPA, just type\n\n    M-x package-install RET iter2 RET\n\nto install it.\n\nAlternatively, installing `iter2` from source is not difficult either.\nFirst, clone the source code:\n\n    $ cd SOME-PATH\n    $ git clone https://github.com/doublep/iter2.git\n\nNow, from Emacs execute:\n\n    M-x package-install-file RET SOME-PATH/iter2\n\n=== Running regression tests\n\nThis is only possible if you have the full source code, e.g. cloned it\nfrom Git as described above.  Just execute `eldev test` from the\n`iter2` directory (you need to have {uri-eldev}[Eldev] installed).\nAll tests must pass, there can be no “expected failures”.\n\n\n== Usage\n\n=== In place of `generator`\n\nJust replace all `iter-defun` with `iter2-defun` and `iter-lambda`\nwith `iter2-lambda`.  And, of course, add\n\n[source]\n----\n(require 'iter2)\n----\n\nsomewhere at the top of your file.  You are done, no other changes are\nneeded.\n\n=== From scratch\n\nPlease refer to {uri-wp-generators}[description in Wikipedia] for\nreasons to use generator functions in general.\n\nTo declare a generator function, use `iter2-defun` or `iter2-lambda`.\nInside the function you can yield control with `iter-yield`.  For\nexample:\n\n[source]\n----\n(iter2-defun unbounded-counter (start)\n  (while t\n    (iter-yield start)\n    (setq start (1+ start))))\n----\n\nYielding can happen anywhere inside generator function with one\nexception: you cannot yield from cleanup forms inside `(unwind-protect\nBODY CLEANUP...)`.  It is also possible to yield values produced by\nanother generator with `iter-yield-from` macro.\n\nTo create an iterator object, call generator function as a usual\nfunction.  Once you have an iterator object, retrieve values from it\nusing `iter2-next`:\n\n[source]\n----\n(let ((it (unbounded-counter 1)))\n  (dotimes (_ 5)\n    (print (iter2-next it))))\n----\n\nYou should always call `iter-close` on iterator object once you no\nlonger need it.  Otherwise, cleanup forms in `unwind-protect` in the\ngenerator may not run.  Iterators produced by our `unbounded-counter`\ndo not really need closing, since `unwind-protect` is never used in\nthat function, but if you write anything that works with arbitrary\ngenerators, keep that in mind.\n\nIf generator function terminates, `iter2-next` will signal condition\n`iter-end-of-sequence` with evaluated value.  For example, this form:\n\n[source]\n----\n(let ((it (funcall (iter2-lambda ()\n                     (iter-yield 1)\n                     2))))\n  (iter2-next it)\n  (iter2-next it))\n----\n\nwill signal `(iter-end-of-sequence . 2)`.  You can use\n`condition-case` to handle this condition.  In simple cases, you can\nuse `iter-do` macro, which parallels `dolist` and always run its\niterator till the end:\n\n[source]\n----\n(iter-do (x (funcall (iter2-lambda ()\n                       (iter-yield 'a)\n                       (iter-yield 'b))))\n  (print x))\n----\n\nOptional second parameter of `iter2-next` is the value that is\nreturned by `iter-yield` inside generator function.  To illustrate:\n\n[source]\n----\n(iter2-defun parrot (value)\n  (while t\n    (setq value (iter-yield value))))\n\n(let ((it (parrot 1)))\n  (print (iter2-next it))  ; first time it is not used\n  (print (iter2-next it 'hello))\n  (print (iter2-next it (list 1 2 3)))\n  (print (iter2-next it)))\n----\n\n[#iter2-next-explained]\n=== `iter2-next` compared to `iter-next`\n\nThe library makes it possible to send signals (`signal`, `error`) to\ngenerator functions and throw to tags (`throw`) in them.  This is an\nimportant feature for reusing generator functions for coroutines.\n\nWhen you use form like\n\n[source]\n----\n(iter-next it FORM)\n----\n\nsomewhere in your code, the `FORM` gets evaluated and then its\n_result_ is passed to the iterator.  If the `FORM` exits nonlocally,\ne.g. by signalling an error, this error happens in the context of\nthe code using the iterator, meaning that code like\n\n[source]\n----\n(iter-defun ...\n  ...\n  (ignore-errors (iter-yield ...)))\n----\n\nis pointless, because no errors will ever be signalled out of\n`iter-yield`.\n\nHowever, if you use `iter2-next`, nonlocal exits of the `FORM` will be\nemitted out of `iter-yield`, at which point generator function may\nprocess it using `condition-case` or `catch`.\n\nAs an example, let’s modify `unbounded-counter` above a bit:\n\n[source]\n----\n(iter2-defun unbounded-counter (start)\n  (while t\n    (setq start (+ start (catch 'skip (iter-yield start) 1)))))\n----\n\nThe following code will now print values 1, 2 and 12:\n\n[source]\n----\n(let ((it (unbounded-counter 1)))\n  (print (iter2-next it))\n  (print (iter2-next it))\n  (print (iter2-next it (throw 'skip 10))))\n----\n\n\n== Tips and tricks\n\n* Since `iter2` is fully compatible with `generator`, they can be used\n  interchangeably or even together, and will produce identical end\n  results, save for any bugs.  Therefore, if you suspect a bug in\n  `iter2`, try replacing `iter2-defun` with `iter-defun` in your\n  generator definition.  Remember, though, that `generator` package\n  also has bugs, e.g. {uri-emacs-bug-26073}[with lambda parameter\n  names matching already bound variable names].\n* Generator functions can only yield “on their own”, it is not allowed\n  to have a called function yield control on their behalf.  For\n  example, this is illegal:\n+\n[source]\n----\n(iter2-defun clever-but-illegal (\u0026rest args)\n  (mapc (lambda (x) (iter-yield x)) args))\n----\n+\nThe package provides a guard against such mistakes.  It is not on by\ndefault, but you can activate it by customizing\n`iter2-detect-nested-lambda-yields`.  It can come in very handy, since\noftentimes nested lambdas are generated by macros (e.g. by `dash.el`)\nwithout you even being aware of that.\n+\nRemember that calling `iter-yield` by its name is also illegal.\nI.e. like this:\n+\n[source]\n----\n(iter2-defun clever-but-illegal-2 (\u0026rest args)\n  (mapc #'iter-yield args))\n----\n+\nUnfortunately, the guard will not detect such things and they will\nfail only at runtime.  Just remember, never ever call `iter-yield` by\nname, always use `(iter-yield ...)` form.\n\n[#save-x]\n=== Current buffer, point etc.\n\nIn general, generator functions must be aware that when `iter-yield`\ngives control back, invoking function can do anything it wants,\nincluding switching to other buffers, moving point, matching regular\nexpressions and so on.  When generator function resumes, its local\nvariables (and dynamic ones it rebound) get their values restored, but\nnot other global state.\n\nHowever, you can use special forms `save-excursion`,\n`save-current-buffer`, `save-restriction` and macro `save-match-data`\nto “separate” generator function buffer and match data state from its\ncaller’s state.  This is probably easier to illustrate with an\nexample:\n\n[source]\n----\n(iter2-defun uses-own-buffer ()\n  (with-temp-buffer\n    (insert \"foo\")\n    (iter-yield 1)\n    (insert \" bar\")\n    (buffer-substring (point-min) (point-max))))\n\n(print (iter-do (_ (uses-own-buffer))\n         (insert \"just a test\")))\n----\n\nThis example doesn’t do anything remotely useful, of course, but it\nshows how generator function and its caller can write each to its own\nbuffer: `with-temp-buffer` internally uses `save-current-buffer`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoublep%2Fiter2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdoublep%2Fiter2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoublep%2Fiter2/lists"}