(defun my-function ()
"..."
'(42 "a" b (x y) "c"))
(defcustom foo 42
"..."
:group 'convenience
:type '(restricted-sexp
:match-alternatives ((lambda (x)
(car (member x (my-function)))))))
Function my-function could provide a different list of possibilities each time (or whenever, based on whatever).
This simple example doesn't provide much in the way of feedback (and no doc strings).
But the idea is this: restricted-sexp is the most general defstruct type-defining construct. This is what the Elisp manual, node Composite Types says:
(restricted-sexp :match-alternatives CRITERIA)
This is the most general composite type construct. The value may
be any Lisp object that satisfies one of CRITERIA. CRITERIA should
be a list, and each element should be one of these possibilities:
A predicate—that is, a function of one argument that returns
either nil or non-nil according to the argument. Using a
predicate in the list says that objects for which the
predicate returns non-nil are acceptable.
A quoted constant—that is, 'OBJECT. This sort of element in
the list says that OBJECT itself is an acceptable value.
For example,
(restricted-sexp :match-alternatives
(integerp 't 'nil))
allows integers, t and nil as legitimate values.
The customization buffer shows all legitimate values using their
read syntax, and the user edits them textually.
In the example I gave, the list of CRITERIA is a singleton, with just this function:
(lambda (x) (car (member x (my-function))))
That function ensures that the value you use for the option is a member of the list that function my-function returns. (And it makes the value be the car of what member returns, i.e., the particular member of the list that the user enters.
When using M-x customize-option you won't be able to set a new value if it isn't one of the values returned by my-function.
Instead of seeing feedback that the value was set to what you entered, you continue to see this help text:
EDITED, shown value does not take effect until you set or save it.
If you provide a valid value then you get confirmation that the value was set.
Admittedly, what you might really want is to provide for completion etc. in the editing field. For that, you would need to use/define an appropriate widget, because Customize, out of the box, provides for completion only for a limited set of defcustom type constructs (files, colors, etc.). You can see which ones by searching for completion starting in node Simple Types.
restricted-sexp is really a wonderful thing. It should be improved by Emacs to be more useful. But even as it is it should be used more, to provide more-specific type checking. Too many users defining defcustoms are a bit lazy, IMO, not bothering to provide a specific type definition even when that might be as simple as using choice. (Too many just use a type of sexp.)