reddit / emacs
Is it possible to assign a variable to plist or alist element and use it wherever a variable is expected in code?
vfclists · 2022-05-15 20:39
I have written a program which stores a class of files in a variable, and in trying to create something similar for another class of files I see that the same code can be used if the variables used by the first class are substituted with those of the second class.
Consider the code below
(setq org-sql-files-plist (plist-put org-sql-files-plist task-kwd
`(:file_path ,full-filename
:filename ,orgfile
:dirpath ,(file-name-directory orgfile)
:category ,(rc/org-global-prop-value "TASK_GROUP")
:title ,(rc/org-global-prop-value "TITLE")
:description ,(rc/org-global-prop-value "TASK_DESC")
:task_id ,task-id
:parent_file ,(rc/org-global-prop-value "PARENT_FILE")
:date_added ,date-added)
))
Say I need to substitute `org-sql-files-plist` with `org-python-files-plist`, but instead of using `org-python-files-plist` in the code I use `(somelist "python-files-list")` where `(somelist "python-files-list")` is set to `org-python-files-plist`, ie not the value of the variable but the variable itself.
It is something like passing variables or pointers to variables in procedural languages.
usaoc · 2022-05-16 04:09
Define a function and pass the list as an argument. Am I missing something?
vfclists · 2022-05-16 08:41
After some consideration I think what I need are macros and a function which passes the variables to those macros.
Consider these 2 sets of variables
Set 1:
(defvar org-sql-files-plist2 nil "Files for org-sql-agenda iter 2.")
(defvar org-sql-files-column-descriptions-plist nil2 "Column names, and display widths of org-sql-files-plist2.")
(defvar org-sql-missing-files nil "Files for org-sql-agenda that are not present.")
Set 2:
(defvar org-taskdocs-files-plist2 nil "Files for org-taskdocs-agenda iter 2.")
(defvar org-taskdocs-files-column-descriptions-plist2 nil "Column names, and display widths of org-taskdocs-files-plist2.")
(defvar org-taskdocs-missing-files nil "Files for taskdocs that are not present.")
The same set of functions are applied with them and they are persisted between sessions.
Set 1:
(add-to-list 'savehist-additional-variables 'org-sql-files-plist2)
(add-to-list 'savehist-additional-variables 'org-sql-files-column-descriptions-plist)
(add-to-list 'savehist-additional-variables 'org-sql-missing-files)
Set 2:
(add-to-list 'savehist-additional-variables 'org-taskdocs-files-plist2)
(add-to-list 'savehist-additional-variables 'org-taskdocs-files-column-descriptions-plist2)
(add-to-list 'savehist-additional-variables 'org-taskdocs-missing-files)
If I want to operate on taskdocs files I should be able to do something like (pseudocode)
(plist-put filesgroup :taskdocs (:files org-tasks-files-plist2 :descriptions org-taskdocs-files-column-descriptions-plist2 :missing org-taskdocs-missing-files))
then I pass `(filesgroup :taskdocs)` to the family of functions which generate the `org-taskdocs-xxxx` variables.
So if I want to generate a new set of variables, say for python files, I simply declare a set of variables with `taskdocs` replaced with `python` and to generate the python files I pass `(filesgroup :python)` to the functions which update the contents of the `org-python-xxx` variables.
I think macros may be what I need here, and if I add an additional level of indirection I should be able to pass just `taskdocs`, `python` or `sql` to a function which will splice the `org-filetype-xxxx` variables into the functions and run them.
usaoc · 2022-05-16 08:52
You don’t need macros, or at least you don’t need to implement them yourself. In Lisp, the `car` and `cdr` of a `cons` *are* references, or more commonly known as generalized variables. See [*Common Lisp: The Language*](https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node80.html) for the theory and [the Elisp manual](https://www.gnu.org/software/emacs/manual/html_node/elisp/Generalized-Variables.html) as well as [the `cl-lib` manual](https://www.gnu.org/software/emacs/manual/html_node/cl/Generalized-Variables.html) for the application. In short: Use `(setf (plist-get PLIST PROP) VALUE)` instead, and nest `plist-get` for nested plists.
vfclists · 2022-05-16 09:39
I think you should read my reply as it expresses what I want to accomplish better than the original question.
I am storing data in variables that need to be persisted between sessions, so when I restart Emacs I should be able to apply `org-sql-files-plist2` and `org-taskdocs-files-plist2` to other functions which need them, without having to update them again.
That is why the functions which use the variables need the variables themselves not their values.
[Macros](https://www.gnu.org/software/emacs/manual/html_node/elisp/Macros.html)
> Macros enable you to define new control constructs and other language features. A macro is defined much like a function, but instead of telling how to compute a value, it tells how to compute another Lisp expression which will in turn compute the value. We call this expression the expansion of the macro.
>
> Macros can do this because they operate on the unevaluated expressions for the arguments, **not on the argument values as functions do**. They can therefore construct an expansion containing these argument expressions or parts of them.
In short I need to know whether I can store the variable names themselves in the plists, not their values, so that the plists values expand to the variable names in the macros.
usaoc · 2022-05-16 09:54
You’re asking an X-Y problem, and I’m alluding to what you *should* do. Normally, you should declare a single plist (or more commonly an alist) variable and store all values into that list, instead of creating dynamic variables. If you need to persist the value, persist that variable. If other functions need to access/mutate the properties, access/mutate that list.
To answer the actual question, yes and no. A “variable name” is syntactic in nature, and therefore cannot be stored. However, you can store a symbol, whose value cell holds the global value of a dynamic variable. Nonetheless, as what I’ve said, the `car` and `cdr` of a `cons` *are* references, just like [the value cell of a symbol](https://www.gnu.org/software/emacs/manual/html_node/elisp/Symbol-Components.html), so there’s no reason to do this.
vfclists · 2022-05-18 08:58
I understand what you mean in that rather than have variables like `org-XXX-files-plist2`
`org-XXX-files-column-descriptions-plist2` `org-XXX-files-missing` for each files group XXXX it will be better to have `(filegroup 'XXXX' 'files')` `(filegroup 'XXXX' 'column-descriptions')` and `(filegroup 'XXXX' 'missing-files')` etc.
Is `(setf (plist-get PLIST PROP) VALUE)` equivalent to `(setq PLIST (plist-put PLIST PROP) VALUE)`, but with the performance and memory benefit of not copying the whole of PLIST then updating or setting the new value?
usaoc · 2022-05-18 10:00
[`plist-put` is destructive](https://www.gnu.org/software/emacs/manual/html_node/elisp/Plist-Access.html), so that’s not the benefit of `setf` here. The benefit of `setf` here is that it deals with generalized variables, so you can arbitrarily nest `plist-get` (this seems to be undocumented, but a `plist-get` form is indeed a generalized variable, and it’s such that the `PLIST` argument can also be a generalized variable) and let `setf` expand to the correct code. You can even define an accessor macro that expands to nested `plist-get`s and `setf` can still expand to the correct code.
(defmacro plist-get-nest (plist &rest props)
(pcase props
(`(,prop . ,rest-props)
`(plist-get-nest (plist-get ,plist ,prop) ,@rest-props))
(_ plist)))