{"id":13894663,"url":"https://github.com/okamsn/loopy","last_synced_at":"2025-07-17T09:33:51.899Z","repository":{"id":37655002,"uuid":"309089800","full_name":"okamsn/loopy","owner":"okamsn","description":"A better looping and iteration macro for Emacs.","archived":false,"fork":false,"pushed_at":"2024-11-22T02:20:51.000Z","size":1974,"stargazers_count":23,"open_issues_count":28,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-22T03:17:46.808Z","etag":null,"topics":["elisp","emacs","emacs-lisp"],"latest_commit_sha":null,"homepage":"","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/okamsn.png","metadata":{"files":{"readme":"README.org","changelog":"CHANGELOG.md","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":"2020-11-01T12:22:08.000Z","updated_at":"2024-09-14T14:45:04.000Z","dependencies_parsed_at":"2023-02-18T13:45:46.026Z","dependency_job_id":"c512ba65-2f74-4771-b23d-28d1d41eb62d","html_url":"https://github.com/okamsn/loopy","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/okamsn%2Floopy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/okamsn%2Floopy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/okamsn%2Floopy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/okamsn%2Floopy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/okamsn","download_url":"https://codeload.github.com/okamsn/loopy/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226248375,"owners_count":17595159,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["elisp","emacs","emacs-lisp"],"created_at":"2024-08-06T18:01:41.404Z","updated_at":"2025-07-17T09:33:51.894Z","avatar_url":"https://github.com/okamsn.png","language":"Emacs Lisp","funding_links":[],"categories":["Emacs Lisp"],"sub_categories":[],"readme":"#+title: Loopy: A Looping and Iteration Macro\n# Make sure to export all headings as such.  Otherwise, some links to\n# sub-headings won’t work.\n#+options: H:6\n# Some parsers require this option to export footnotes.\n#+options: f:t\n\n# Badges\n[[https://elpa.nongnu.org/nongnu/loopy.html][file:https://elpa.nongnu.org/nongnu/loopy.svg]]\n[[https://elpa.nongnu.org/nongnu-devel/loopy.html][file:https://elpa.nongnu.org/nongnu-devel/loopy.svg]]\n[[https://melpa.org/#/loopy][file:https://melpa.org/packages/loopy-badge.svg]]\n[[https://stable.melpa.org/#/loopy][file:https://stable.melpa.org/packages/loopy-badge.svg]]\n\n-----\n\n~loopy~ is a macro meant for iterating and looping.  It is similar in usage to\n[[https://www.gnu.org/software/emacs/manual/html_node/cl/Loop-Facility.html#Loop-Facility][~cl-loop~]] but uses symbolic expressions rather than keywords.\n\nFor most use cases, ~loopy~ should be a nice substitute for ~cl-loop~ and\ncomplementary to the features provided by the [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Sequences-Arrays-Vectors.html][Seq]] and [[https://www.gnu.org/software/emacs/manual/html_node/cl/index.html][CL]] libraries and Emacs's\nregular looping and mapping features.\n\nFor detailed information, see [[file:doc/loopy-doc.org][the documentation file]].  This README is just an\noverview.\n\nSee also the extension package [[https://github.com/okamsn/loopy-dash][Loopy Dash]], which  provides destructuring using\nthe library =dash.el= and is described in more detail below.\n\n-----\n#+begin_center\n*NOTE*: Loopy is still in its latter middle stages.\\\\\nConstructive criticism is welcome.  If you see a place for improvement,\nplease let me know.\n#+end_center\n-----\n\n _Recent breaking changes:_\n - Unreleased:\n   - =set= now warns when it is not given a value.  In the future, it will\n     signal an error.\n   - ~loopy-command-parsers~ and ~loopy-aliases~ are deprecated in favor of\n     a single hash table in the new user option ~loopy-parsers~.  This\n     simplified the code and will make adding local overrides easier.\n   - =when= and =unless= are now implemented separately, fixing when the\n     commands are aliased.\n - Version 0.14.0:\n   - Conflicting initialization values for accumulation variables now signal\n     a warning.  In the future, they will signal an error.\n   - The positional arguments to the =numbers= command have been removed,\n     being deprecated since version 0.12.0.\n   - Some built-in aliases have been made obsolete and will be removed from the\n     list of built-in aliases in the future.  They can still be added to the\n     list of known aliases using ~loopy-defalias~.  See the changelog for more\n     information.\n   - =seq= and =seq-ref= now work on generic sequences and are separate commands\n     from =sequence= and =sequence-ref=.  =sequence-index= now uses\n     ~seq-length~.\n   - Improved consistency of some keyword arguments:\n     - The =:unique= keyword argument of the =map= and =map-ref= commands can now\n       be evaluable at run time.\n     - The =:close= argument of the =iter= command is now evaluable, instead of\n       only being used during macro expansion.\n     - The =:close= argument of the =iter= command is now evaluated at the\n       beginning of the loop.\n     - The =:on-failure= argument of the =find= command is now evaluated at the\n       beginning of the loop.\n   - Changed back to the old, slightly slower behavior of always initializing\n     iteration variables to ~nil~, instead of sometimes initializing to the\n     expected value during the first iteration step.  This affects =cons=,\n     =cycle=, =iter=, =numbers=, =seq-index=, and =substream=.\n - See the [[https://github.com/okamsn/loopy/blob/master/CHANGELOG.md][change log]] for less recent changes.\n\n# This auto-generated by toc-org.\n* Table of Contents                                                :TOC:noexport:\n- [[#introduction][Introduction]]\n- [[#similar-libraries][Similar Libraries]]\n- [[#how-to-install][How to Install]]\n- [[#multiple-kinds-of-destructuring][Multiple Kinds of Destructuring]]\n- [[#loop-commands-in-arbitrary-code][Loop Commands in Arbitrary Code]]\n- [[#adding-custom-commands][Adding Custom Commands]]\n- [[#comparing-to-cl-loop][Comparing to =cl-loop=]]\n- [[#real-world-examples][Real-World Examples]]\n\n* Introduction\n\nThe ~loopy~ macro is used to generate code for a loop, similar to ~cl-loop~.\nUnlike ~cl-loop~, ~loopy~ uses parenthetical expressions instead of \"clauses\".\n\n#+begin_src emacs-lisp\n  ;; A simple usage of `cl-loop':\n  (cl-loop for i from 1 to 10\n           if (cl-evenp i) collect i into evens\n           else collect i into odds\n           end ; This `end' keyword is optional here.\n           finally return (list odds evens))\n\n  ;; How it could be done using `loopy':\n  (loopy (numbers i :from 1 :to 10)\n         (if (cl-evenp i)\n             (collect evens i)\n           (collect odds i))\n         (finally-return odds evens))\n#+end_src\n\n~loopy~ supports destructuring for iteration commands like =list= and\naccumulation commands like =sum= or =collect=.\n\n#+begin_src emacs-lisp\n  ;; Summing the nth elements of sub-arrays:\n  ;; =\u003e (8 10 12 14 16 18)\n  (loopy (list list-elem '(([1 2 3] [4 5 6])\n                           ([7 8 9] [10 11 12])))\n         (sum ([sum1 sum2 sum3] [sum4 sum5 sum6])\n              list-elem)\n         (finally-return sum1 sum2 sum3 sum4 sum5 sum6))\n\n  ;; Separate the elements of sub-list:\n  ;; =\u003e ((1 3) (2 4))\n  (loopy (list i '((1 2) (3 4)))\n         (collect (elem1 elem2) i)\n         (finally-return elem1 elem2))\n#+end_src\n\nThe ~loopy~ macro is configurable and extensible.  In addition to writing one's\nown \"loop commands\" (such as =list= in the example above), by using \"flags\", one\ncan choose whether to instead use ~pcase-let~, ~seq-let~, or even the Dash\nlibrary for destructuring.\n\n#+begin_src emacs-lisp\n  ;; Use `pcase' to destructure array elements:\n  ;; =\u003e ((1 2 3 4) (10 12 14) (11 13 15))\n  (loopy (flag pcase)\n         (array (or `(,car . ,cdr) digit)\n                [1 (10 . 11) 2 (12 . 13) 3 4 (14 . 15)])\n         (if digit\n             (collect digits digit)\n           (collect cars car)\n           (collect cdrs cdr))\n         (finally-return digits cars cdrs))\n\n  ;; Using the default destructuring:\n  ;; =\u003e ((1 2 3 4) (10 12 14) (11 13 15))\n  (loopy (array elem [1 (10 . 11) 2 (12 . 13) 3 4 (14 . 15)])\n         (if (numberp elem)\n             (collect digits elem)\n           (collect (cars . cdrs) elem))\n         (finally-return digits cars cdrs))\n#+end_src\n\nVariables like =cars=, =cdrs=, and =digits= in the example above are\nautomatically ~let~-bound so as to not affect code outside of the loop.\n\n~loopy~ has arguments for binding (or not binding) variables, executing code\nbefore or after the loop, executing code only if the loop completes, and for\nsetting the macro's return value (default: ~nil~).  This is in addition to the\nlooping features themselves.\n\nAll of this makes ~loopy~ a useful and convenient choice for looping and\niteration.\n\n* Similar Libraries\n\nLoopy is not the only Lisp library that uses parenthetical expressions instead of\nkeyword clauses (as in ~cl-loop~).  [[https://common-lisp.net/project/iterate/][Iterate]] and [[https://github.com/Shinmera/for/][For]] are two examples from\nCommon Lisp.\n\n#+begin_src emacs-lisp\n  ;; Collecting 10 random numbers:\n\n  ;; cl-loop (Emacs Lisp)\n  (cl-loop repeat 10 collect (random 10))\n\n  ;; loopy (Loopy)\n  (loopy (repeat 10) (collect (random 10)))\n\n  ;; iterate (Common Lisp)\n  (iterate (repeat 10) (collect (random 10)))\n\n  ;; for (Common Lisp)\n  (for:for ((i repeat 10) (randoms collecting (random 10))))\n\n#+end_src\n\nGenerally, all of the packages handle basic use cases in similar ways.  One\nlarge difference is that ~iterate~ can embed its looping constructs in arbitrary\ncode.  Loopy currently provides this feature as a separate macro,\n~loopy-iter~, which expands looping constructs using ~macroexpand~\n(see [[*Loop Commands in Arbitrary Code][Loop Commands in Arbitrary Code]] in this README).\n\nLoopy is not yet feature complete.  Please request features or report problems\nin this project’s [[https://github.com/okamsn/loopy/issues][issues tracker]].  While basic uses are covered, some of the\nmore niche features of ~cl-loop~ and ~iterate~ are still being added.\n\n* How to Install\n\nLoopy can be installed from [[https://elpa.nongnu.org/nongnu/loopy.html][Non-GNU ELPA]] and [[https://melpa.org/#/loopy][MELPA]] as the package =loopy=.  The\noptional package =loopy-dash= can be installed to enable using the Dash library\nfor destructuring (instead of other methods).\n\n#+begin_src emacs-lisp\n  (use-package loopy)\n\n  ;; Optional support for destructuring with Dash.\n  (use-package loopy-dash\n    :after (loopy)\n    :demand t)\n#+end_src\n\nTo load all of the alternative destructuring libraries (see section [[*Multiple Kinds of Destructuring][Multiple\nKinds of Destructuring]]) and the alternative macro form (see section [[*Loop Commands in Arbitrary Code][Loop\nCommands in Arbitrary Code]]), use\n\n#+begin_src emacs-lisp\n  (use-package loopy\n    :config\n    (require 'loopy-iter)\n    (require 'loopy-pcase)\n    (require 'loopy-seq))\n\n  (use-package loopy-dash\n    :after (loopy)\n    :demand t)\n#+end_src\n\n* Multiple Kinds of Destructuring\n\nThe default destructuring system is a super-set of what =cl-lib= provides\nand is described in the section [[https://github.com/okamsn/loopy/blob/master/doc/loopy-doc.org#basic-destructuring][Basic Destructuring]] in the documentation.\n\nIn addition to the built-in destructuring style, ~loopy~ can optionally use\ndestructuring provided by ~pcase-let~, ~seq-let~, and the =dash= library.  This\nprovides greater flexibility and allows you to use destructuring patterns that\nyou're already familiar with.\n\nThese features can be enabled with \"flags\", described in the section [[https://github.com/okamsn/loopy/blob/master/doc/loopy-doc.org#using-flags][Using Flags]]\nin the documentation.\n\nHere are a few examples that demonstrate how ~loopy~ can use destructuring with\naccumulation commands.\n\n#+begin_src emacs-lisp\n  (require 'loopy-dash)\n  ;; =\u003e (((1 (2 3)) (4 (5 6))) ; whole\n  ;;     (1 4)                 ; i\n  ;;     (3 6))                ; k\n  (loopy (flag dash)\n         (list elem '((1 (2 3)) (4 (5 6))))\n         (collect (whole \u0026as i (_ k)) elem)\n         (finally-return whole i k))\n\n  ;; = \u003e ((3 5) (4 6))\n  (loopy (flag dash)\n         (list (\u0026plist :a a  :b b)\n               '((:a 3  :b 4 :c 7) (:g 8 :a 5 :b 6)))\n         (collect a-vals a)\n         (collect b-vals b)\n         (finally-return a-vals b-vals))\n\n  (require 'loopy-pcase)\n  ;; =\u003e ((1 4) (3 6))\n  (loopy (flag pcase)\n         (list elem '((1 (2 3)) (4 (5 6))))\n         (collect `(,a (,_ ,b)) elem)\n         (finally-return a b))\n\n  ;; =\u003e ((1 6) (3 8) ([4 5] [9 10]))\n  (require 'loopy-seq)\n  (loopy (flag seq)\n         (list elem '([1 2 3 4 5] [6 7 8 9 10]))\n         (collect [a _ b \u0026rest c] elem)\n         (finally-return a b c))\n#+end_src\n\nFor more on how =dash= does destructuring, see their documentation on the [[https://github.com/magnars/dash.el#-let-varlist-rest-body][-let]]\nexpression.\n\n* Loop Commands in Arbitrary Code\n\nThe macro ~loopy-iter~ can be used to embed loop commands in arbitrary code.  It\nis similar in use to Common Lisp's Iterate macro, but it is not a port of\nIterate to Emacs Lisp.\n\n#+begin_src emacs-lisp\n  (require 'loopy-iter)\n\n  ;; =\u003e ((1 2 3) (-3 -2 -1) (0))\n  ;; Things to node:\n  ;; - `accum-opt' produces more efficient accumulations for names variables\n  ;; - `cycling' is another name for `repeat'\n  ;; =\u003e ((-9 -8 -7 -6 -5 -4 -3 -2 -1)\n  ;;     (0)\n  ;;     (1 2 3 4 5 6 7 8 9 10 11))\n  (loopy-iter (accum-opt positives negatives zeroes)\n              (numbering i :from -10 :to 10)\n              ;; Normal `let' and `pcase', not Loopy constructs:\n              (let ((var (1+ i)))\n                (pcase var\n                  ((pred cl-plusp)  (collecting positives var))\n                  ((pred cl-minusp) (collecting negatives var))\n                  ((pred zerop)     (collecting zeroes var))))\n              (finally-return negatives zeroes positives))\n\n  ;; =\u003e 6\n  (loopy-iter (listing elem '(1 2 3))\n              (funcall #'(lambda (x) (summing x))\n                       elem))\n#+end_src\n\nFor more on this, [[https://github.com/okamsn/loopy/blob/master/doc/loopy-doc.org#the-loopy-iter-macro][see the documentation]].\n\n* Adding Custom Commands\n:PROPERTIES:\n:CUSTOM_ID: adding-custom-commands\n:END:\n\nIt is easy to create custom commands for Loopy.  To see how, see the section\n[[https://github.com/okamsn/loopy/blob/master/doc/loopy-doc.org#custom-commands][Custom Commands]] in the documentation.\n\n* Comparing to =cl-loop=\n:PROPERTIES:\n:CUSTOM_ID: how-does-it-compare-to-other-approaches\n:END:\n\nSee the documentation page [[https://github.com/okamsn/loopy/blob/master/doc/loopy-doc.org#comparing-to-cl-loop][Comparing to ~cl-loop~]].  See also the wiki page [[https://github.com/okamsn/loopy/wiki/speed-comparisons][Speed\nComparisons]].\n\n\n* Real-World Examples\n\nSee the wiki page [[https://github.com/okamsn/loopy/wiki/Examples][Examples]].\n\n# Local Variables:\n# org-link-file-path-type: relative\n# org-adapt-indentation: nil\n# End:\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fokamsn%2Floopy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fokamsn%2Floopy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fokamsn%2Floopy/lists"}