{"id":13546352,"url":"https://github.com/tpapp/let-plus","last_synced_at":"2025-04-02T18:30:37.964Z","repository":{"id":66832022,"uuid":"1788628","full_name":"tpapp/let-plus","owner":"tpapp","description":"destructuring extension of let*","archived":true,"fork":false,"pushed_at":"2018-03-07T13:01:24.000Z","size":50,"stargazers_count":53,"open_issues_count":0,"forks_count":9,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-27T04:07:07.948Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tpapp.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":"LICENSE_1_0.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2011-05-23T15:31:39.000Z","updated_at":"2025-02-16T12:48:59.000Z","dependencies_parsed_at":"2023-02-22T19:16:08.627Z","dependency_job_id":null,"html_url":"https://github.com/tpapp/let-plus","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tpapp%2Flet-plus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tpapp%2Flet-plus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tpapp%2Flet-plus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tpapp%2Flet-plus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tpapp","download_url":"https://codeload.github.com/tpapp/let-plus/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246869609,"owners_count":20847159,"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":[],"created_at":"2024-08-01T12:00:35.778Z","updated_at":"2025-04-02T18:30:37.686Z","avatar_url":"https://github.com/tpapp.png","language":"Common Lisp","funding_links":[],"categories":["Common Lisp"],"sub_categories":[],"readme":"#+TITLE: =let+=: destructuring extension of =let*=\n#+AUTHOR: Tamás K. Papp\n\n*IMPORTANT* This library is [[https://tpapp.github.io/post/orphaned-lisp-libraries/][unmaintained]].\n\nThis library implements the =let+= macro, which is a dectructuring\nextension of =let*=.\n\n* Highlights\n\n- clean, consistent syntax and small implementation (less than 300 LOC, not counting tests)\n\n- placeholder macros allow editor hints and syntax highlighting\n\n- =\u0026ign= for ignored values (in forms where that makes sense)\n\n- very easy to extend\n\n* Similar libraries\n\nThis library was inspired by Gary King's excellent [[http://common-lisp.net/project/metabang-bind/][metabang-bind]].  I\nhave been using the latter for years now, but at some point I decided\nto write a library of my own, aiming for a cleaner syntax, more\nconcise implementation and a more consistent interface (whether I have\nsucceeded is of course a matter of judgement --- try [[http://common-lisp.net/project/metabang-bind/][metabang-bind]] to\nsee if you like it better).\n\nIn my opinion the main advantages of this library, compared to\n[[http://common-lisp.net/project/metabang-bind/][metabang-bind]], are the placeholder macros which provide editor hints\nand the more consistent syntax of destructuring forms.  In particular,\nwhen both read-write and read-only forms are available the latter\nalways have the =-r/o= suffix, =\u0026flet= and =\u0026labels= resemble the\nCommon Lisp syntax more closely, and the library should be easier to\nextend.\n\nYou can find other pattern matching libraries on [[http://www.cliki.net/pattern%20matching][cliki]].\n\n* Syntax\n\n#+BEGIN_SRC lisp\nlet+ ({binding}*) body*\n#+END_SRC\nwhere\n#+BEGIN_SRC lisp\nbinding ::= symbol || (form [init-form])\n#+END_SRC\n\n=LET+= is recursive: each binding is in the scope of the previous\nones.  Forms ignore =\u0026ign= variables (where applicable).\n\n** Built-in forms\n\nForms which provide both read-write and read-only access are available as =\u0026form= and =\u0026form-r/o=.  The first one always uses symbol macros, so you can use =setf=.  The second one reads the values at the beginning of the list from value: you can change these variables after that without having any effect on the original value.  Read-only forms may also provide a slight increase in speed, and promote good style --- you can use them to signal that you will not change the original structure.\n\nThe following forms are defined:\n\n- =var=, =(var)=, =(var value)= :: These behave just like they do in =let*=.\n\n- =(list value)= :: When =list= is not recognized as any of the forms below, it is simply destructured using =destructuring-bind=.  =\u0026ign= are ignored.  Example:\n#+BEGIN_SRC lisp\n(let+ (((a (b \u0026optional (c 3)) \u0026ign \u0026key (d 1 d?)) '(1 (2) 7 :d 4)))\n  (list a b c d d?))  ; =\u003e (1 2 3 4 T)\n#+END_SRC\n\n- =((\u0026slots slot*) value)=, also =\u0026slots-r/o= :: Similarly to =with-slots=, each =slot= has the syntax =variable= or =(variable)= (for these, the variable name is also used for the slot name) or =(variable slot-name)=.  =\u0026slots-r/o= provides read-only bindings.\n\n     Example:\n#+BEGIN_SRC lisp\n(defclass foo-class ()\n  ((a :accessor a :initarg :a)\n   (b :accessor b-accessor :initarg :b)))\n\n(let+ (((\u0026slots a (my-b b)) (make-instance 'foo-class :a 1 :b 2)))\n  (list a my-b))  ; =\u003e (1 2)\n#+END_SRC\n\n- =((\u0026accessors accessor*) value)=, also =\u0026accessors-r/o= :: Syntax similar to =\u0026slots=, but uses accessors.  Continuing the example above:\n#+BEGIN_SRC lisp\n(let+ (((\u0026accessors a (b b-accessor)) (make-instance 'foo-class :a 1 :b 2)))\n  (list a b))  ; =\u003e (1 2)\n#+END_SRC\n\n- =((\u0026structure conc-name slot*) value)=, also =\u0026structure-r/o= :: Slot access for structures.  =Conc-name= is prepended to the accessors (you need to include the =-= if there is one).  Example:\n#+BEGIN_SRC lisp\n(defstruct foo-struct c d)\n(let+ (((\u0026structure foo-struct- c (my-d d)) (make-foo-struct :c 3 :d 4)))\n  (list c my-d))  ; =\u003e (3 4)\n#+END_SRC\n\n- =((\u0026values value*) form)= :: Similar to =multiple-value-bind=.  =\u0026ign= are ignored.  Example:\n#+BEGIN_SRC lisp\n(let+ (((\u0026values a \u0026ign b) (values 1 2 3)))\n  (list a b))  ; =\u003e (1 3)\n#+END_SRC\n\n- =(array value)= (only read-only version) :: The array is\n     destructured to the given elements, =\u0026ign= are ignored.  Indexes\n     use row-major access, determined at macroexpansion time.\n     Example:\n#+BEGIN_SRC lisp\n(let+ ((#(a \u0026ign b) (vector 1 2 3)))\n  (list a b))  ; =\u003e (1 3)\n#+END_SRC\n\n- =((\u0026array-elements (variable subscript*)*) value)=, also =\u0026array-elements-r/o= :: Array elements with given subscripts are assigned to the variables.  Example:\n#+BEGIN_SRC lisp\n(let+ (((\u0026array-elements (a 0 1)\n                         (b 2 0))\n        #2A((0 1)\n            (2 3)\n            (4 5))))\n  (list a b))  ; =\u003e (1 4)\n#+END_SRC\n\n- =((\u0026flet name lambda-list forms*))=, also =\u0026labels= :: Function bindings.  These have no value form. =\u0026labels= allows the function to refer to itself -- note that since =let+= is always recursive, this is the only difference between the two forms.  Example:\n#+BEGIN_SRC lisp\n(let+ (((\u0026flet add2 (x)\n          (+ x 2))))\n  (add2 5))  ; =\u003e 7\n#+END_SRC\n\n- =((\u0026plist (variable key [default])*)=, also =\u0026plist-r/o= :: Access to property lists.  When =key= is not given, =variable= is used instead, and =default= is used if the element does not exist in the value (note that default may be evaluated multiple times when using the read-write form which uses =symbol-macrolet=).  Example:\n#+BEGIN_SRC lisp\n(let+ (((\u0026plist a (my-b b) (c nil 3)) '(a 1 b 2)))\n  (list a my-b c))  ; =\u003e (1 2 3)\n#+END_SRC\n\n- =(((\u0026hash-table (variable key [default])*)=, also =\u0026hash-table-r/o= :: Access to the elements of hash tables, the semantics is the same as =\u0026plist=.\n\n- =(\u0026complex real imaginary)= :: Destructures complex numbers.\n\n** Nesting\n\nYou can nest =let+= expressions when it makes sense (it doesn't always, especially for read/write slots, the read only form should work).  For example,\n#+BEGIN_SRC lisp\n(let+ ((#((\u0026complex a b)) (vector (complex 1 2))))\n  (list a b))\n#+END_SRC\nshould destructure the complex number that is the single element in the vector.\n\nIf you find that =let+= does not nest properly, please report it as a bug.\n\n** Convenience macros\n\n- =(defun+ name (argument*) form*)=, also =(lambda (argument*) form*)= :: Work like =defun= and =lambda=, but arguments are destructured using =let+=.  Example:\n#+BEGIN_SRC lisp\n(defun+ foo ((\u0026plist a b c) #(d e))\n  (list a b c d e))\n\n(foo '(a 1 b 2 c 3) #(4 5))  ; =\u003e (1 2 3 4 5)\n#+END_SRC\nSee also =\u0026labels+= and =\u0026lambda+=.\n\n- =define-structure-let+= :: Can be used to provide destructuring forms for structures.\n\n** Other forms\n\n- =(\u0026once-only symbols ...)= and =(\u0026with-gensyms symbols)= are useful for writing macros.\n\n* Extensions\n\nExtending =let-plus= is very easy: if you want to use a form that\nresembles a list, you just have to define a method for\n=let+-expansion-for-list=.  There is a macro that helps you with that,\ncalled =define-let+-expansion=.  If the library didn't have\n=\u0026complex=, we could define destructuring for the form like this:\n\n#+BEGIN_SRC lisp\n(define-let+-expansion (\u0026complex (x y))\n  \"Access real and imaginary part of the value.  Read-only.\"\n  `(let ((,x (realpart ,value))\n         (,y (imagpart ,value)))\n     ,@body))\n#+END_SRC\nSome highlights:\n\n- this macro defines a \"placeholder\" macro =\u0026complex= that should\n  help with editor hints, but has no other purpose (it is not used in\n  the expansion),\n- the macro is anaphoric, capturing =value= (the value form) and\n  =body= (the body inside the =let+= form), you can customize both of\n  this using keyword arguments,\n- unless required otherwise, =value= is wrapped in =once-only=\n  preventing multiple evaluations of the same form.  See the arguments =:uses-value?= and =:once-only?= for =define-let+-expansion=.\n\nIf you want to extend =let+= with forms that are not lists (eg like\nthe array syntax above), have a look at =let+-expansion=.\n\n* Reporting bugs\n\nPlease open an [[https://github.com/tpapp/let-plus/issues][issue]] on Github for bugs.  Extensions are also welcome,\neither as forks or small code snippets submitted as issues.  Wishlist\nitems are also welcome!\n\nI ask you not to report bugs via e-mail if you can avoid it.  Tracking\nbugs on Github makes it less likely that they get lost.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftpapp%2Flet-plus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftpapp%2Flet-plus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftpapp%2Flet-plus/lists"}