I'd like to create a customizable variable foo-paths with certain options.

(defcustom foo-paths nil
  "Paths for foo-related things."
  :group 'foo
  :options '(foo-1 foo-2)
  :type '(alist :key-type symbol :value-type (file :must-match t)))

I'd like for the user to only be able to provide values for the specified keys in :options, not to add new options.

The manual doesn't offer a way to do this--the user can add new elements to the alist through the Customize interface.

Is there a way to achieve this? The functionality may not be impacted by the addition of new alist elements, but it seems strange that there's no way to specify a variable that has certain options and only those options.

  • An alist is extensible, by definition. See custom type list, which specifies a list of a prescribed length and element types. – Drew Feb 10 '17 at 14:57

Only a partial answer. The :must-match requirement seems broken (a bug?).

An alist is extensible by definition. And :options does not enforce using its values.

Custom type list specifies a list of prescribed length and element types. Use repeat of list. In other words,repeat lists of two elements of given types. For your permissible keys, use choice with const.

(defcustom foo-paths nil
  "Paths for foo-related things."
  :group 'foo
  :type '(repeat (list (choice (const foo-1) (const foo-2))
                       (file :must-match t))))

You can also do something like this:

(defcustom foo-paths nil
  "Paths for foo-related things."
  :group 'foo
  :options '(foo-1 foo-2)
  :type '(alist
          :key-type (choice (const foo-1) (const foo-2))
          :value-type (file :must-match t)))

(Based on your comments, it's not very clear what you really want.)

  • TBH, this is not as good as the alist type. With the alist, the keys specified in :options are shown be default; here, they are not. Also, the user is still able to add extraneous elements with INS in the Customize interface. – Tianxiang Xiong Feb 10 '17 at 21:26
  • '(list (cons (const :tag "Foo-1 path" foo-1) (file :must-match t)) (cons (const :tag "Foo-2 path" foo-2) (file :must-match t))) comes close, but I need to provide an initial value that fits, so it needs to be something like: ((foo-1 . "") (foo-2 . "")). Also, the Customize interface will say Cons-cell in for each list element. – Tianxiang Xiong Feb 10 '17 at 21:35
  • Too bad. You can use alist or you can use repeat or you can use restricted-sexp. There are multiple possibilities. That just-what-you-want is not offered out of the box is unfortunate. File an enhancement request: M-x report-emacs-bug. – Drew Feb 11 '17 at 2:28
  • repeat means you can add more elements (it is a list). But list specifies that each element is locked into only N elements of the specified types. The INS you are referring to is just about adding more elements to the overall value (a list). – Drew Feb 11 '17 at 2:29
  • If you want each list element to be a dotted pair then use cons instead of list. Same idea: you specify the types of the elements of the pair. The point is that both of these satisfy your requirement of not letting the user "add new options". The alist type, which you now say is "better", does not satisfy your own requirement. You need to decide what your question really is. – Drew Feb 11 '17 at 2:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.