In many programming languages, if one wants a variable that can take a discrete number of values, one has the capacity to define the permissible values in advance, using something like an enum.

Does elisp have any functionality like that?

My use case: I'm using the same configuration file across multiple computers. I'm reading an identifier from an environment variable, and I'd really like to store that in an elisp variable as a sum type, so that I can do something like

(cond ((eq which-computer home) (do-home-config))
      ((eq which-computer office) (do-office-config))
      (t (message "unknown computer")))

where which-computer is a variable that can only take the values home and office.

Is that a thing one can do in elisp?

(I don't really know what to tag this question, since apparently using the elisp tag is, like, verboten.)

  • If the valid values are a fixed set, but you're setting it from an arbitrary env var, then what happens when that var does not have a valid value, and how would that be different to handling unknown values with your t case in the cond expression? What do actually want to happen in that scenario? (Error? Message? Silently ignore it?) – phils Jan 9 at 3:48
  • n.b. I'm partly driving at the point that I would expect assigning an invalid value to be an error, but I doubt you actually want your Emacs config to fail to load if you happened to not have some environment variable set with a particular value. – phils Jan 9 at 4:29
  • @phils answered the question well. In a nutshell, in Lisp variables have no type; only values are typed. Your conditional code correctly tests the value of your variable. You were just lacking the use of getenv to get the value from the environment. Phils mentions cl-deftype. There's also Elisp defcustom, which defines the "valid" types of a variable's value, but nothing stops assigning an invalid value to it. – Drew Jan 9 at 5:45
  • A defcustom type of choice comes close to defining a variable whose value should be a sum/union type. (But again, nothing prevents a value that doesn't follow the type spec.) See the Elisp manual, node Composite Types for choice, and node Customization for the overall chapter about defcustom. But such typing applies only to defcustom (options), not to defvar (unfortunately, IMO). – Drew Jan 9 at 5:51

Elisp variables aren't declared with a type. You can test a variable to determine the type of its current value; but if you're using setq or similar to assign values, then nothing prevents you from assigning values of arbitrary types to any given variable.

cl-deftype and friends do allow you to define a custom type which allows a fixed set of values, and you can then use things like cl-typep, cl-typecase, cl-etypecase, and cl-check-type with that type; but I honestly don't think this is a useful complication for your particular use-case.

You can read more about these from: C-hig (cl)Type Predicates

Personally I'm not sure you even need to be storing a variable at all, if this is your sole use-case for it.

(let ((computer (getenv "MY_COMPUTER_TYPE")))
  (cond ((equal computer "home") (do-home-config))
        ((equal computer "office") (do-office-config))
        (t (message "unknown computer"))))

If you were going to store a variable, I'd probably use much the same approach. The following would result in a variable which was either home or office or nil.

(defconst my-computer-type
  (let ((computer (getenv "MY_COMPUTER_TYPE"))
        (allowed '("home" "office")))
    (and (member computer allowed)
         (intern computer)))
  "Symbol for my computer type.")

(cond ((eq my-computer-type 'home) (do-home-config))
      ((eq my-computer-type 'office) (do-office-config))
      (t (message "unknown computer")))

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.