Next: , Previous: , Up: Specifiers   [Contents][Index]


48.10 Examples of Specifier Usage

Now let us present an example to clarify the theoretical discussions we have been through. In this example, we will use the general specifier functions for clarity. Keep in mind that many types of specifiers, and some other types of objects that are associated with specifiers (e.g. faces), provide convenience functions making it easier to work with objects of that type.

Let us consider the background color of the default face. A specifier is used to specify how that color will appear in different domains. First, let’s retrieve the specifier:

(setq sp (face-property 'default 'background))
    ⇒   #<color-specifier 0x3da>
(specifier-specs sp)
    ⇒   ((#<buffer "device.c"> (nil . "forest green"))
                 (#<window on "Makefile" 0x8a2b> (nil . "hot pink"))
                 (#<x-frame "emacs" 0x4ac> (nil . "puke orange")
                                           (nil . "moccasin"))
                 (#<x-frame "VM" 0x4ac> (nil . "magenta"))
                 (global ((tty) . "cyan") (nil . "white"))
                )

Then, say we want to determine what the background color of the default face is for the window currently displaying the buffer ‘*scratch*’. We call

(get-buffer-window "*scratch*")
    ⇒ #<window on "*scratch*" 0x4ad>
(window-frame (get-buffer-window "*scratch*"))
    ⇒ #<x-frame "emacs" 0x4ac>
(specifier-instance sp (get-buffer-window "*scratch*"))
    ⇒ #<color-instance moccasin 47=(FFFF,E4E4,B5B5) 0x6309>

Note that we passed a window to specifier-instance, not a buffer. We cannot pass a buffer because a buffer by itself does not provide enough information. The buffer might not be displayed anywhere at all, or could be displayed in many different frames on different devices.

The result is arrived at like this:

  1. First, we look for a specification matching the buffer displayed in the window, i.e. ‘*scratch*’. There are none, so we proceed.
  2. Then, we look for a specification matching the window itself. Again, there are none.
  3. Then, we look for a specification matching the window’s frame. The specification (#<x-frame "emacs" 0x4ac> . "puke orange") is found. We call the instantiation method for colors, passing it the locale we were searching over (i.e. the window, in this case) and the instantiator (‘"puke orange"’). However, the particular device which this window is on (let’s say it’s an X connection) doesn’t recognize the color ‘"puke orange"’, so the specification is rejected.
  4. So we continue looking for a specification matching the window’s frame. We find ‘(#<x-frame "emacs" 0x4ac> . "moccasin")’. Again, we call the instantiation method for colors. This time, the X server our window is on recognizes the color ‘moccasin’, and so the instantiation method succeeds and returns a color instance.

Here’s another example, which implements something like GNU Emacs’s “frame-local” variables.

;; Implementation

;; There are probably better ways to write this macro
;; Heaven help you if VAR is a buffer-local; you will become very
;; confused.  Probably should error on that.
(defmacro define-frame-local-variable (var)
  "Make the unbound symbol VAR become a frame-local variable."
  (let ((val (if (boundp var) (symbol-value var) nil)))
    `(progn
      (setq ,var (make-specifier 'generic))
      (add-spec-to-specifier ,var ',val 'global))))

;; I'm not real happy about this terminology, how can `setq' be a defun?
;; But `frame-set' would have people writing "(frame-set 'foo value)".
(defun frame-setq (var value &optional frame)
  "Set the local value of VAR to VALUE in FRAME.

FRAME defaults to the selected frame."
  (and frame (not (framep frame))
       (error 'invalid-argument "FRAME must be a frame", frame))
  (add-spec-to-specifier var value (or frame (selected-frame))))

(defun frame-value (var &optional frame)
  "Get the local value of VAR in FRAME.

FRAME defaults to the selected frame."
  (and frame (not (framep frame))
       (error 'invalid-argument "FRAME must be a frame", frame))
  ;; this is not just a map from frames to values; it also falls back
  ;; to the global value
  (specifier-instance var (or frame (selected-frame))))

;; for completeness
(defun frame-set-default (var value)
  "Set the default value of frame-local variable VAR to VALUE."
  (add-spec-to-specifier var value 'global))

(defun frame-get-default (var)
  "Get the default value of frame-local variable VAR."
  (car (specifier-specs var 'global)))

Now you can execute the above definitions (eg, with eval-last-sexp) and switch to *scratch* to play. Things will work differently if you already have a variable named foo.

;; Usage

foo
error→ Symbol's value as variable is void: foo

(define-frame-local-variable foo)
⇒ nil

;; the value of foo is a specifier, which is an opaque object;
;; you must use accessor functions to get values

foo
⇒ #<generic-specifier global=(nil) 0x4f5cb>

;; since no frame-local value is set, the global value (which is the
;; constant `nil') is returned
(frame-value foo)
⇒ nil

;; get the default explicitly
(frame-get-default foo)
⇒ nil

;; get the whole specification list
(specifier-specs foo 'global)
⇒ (nil)

;; give foo a frame-local value

(frame-setq foo 'bar)
⇒ nil

;; access foo in several ways

;; Note that the print function for this kind of specifier only
;; gives you the global setting.  To get the full list of specs for
;; debugging or study purposes, you must use specifier-specs or
;; specifier-spec-list.
foo
⇒ #<generic-specifier global=(nil) 0x4f5cb>

;; get the whole specification list
(specifier-specs foo)
⇒ ((#<x-frame "Message" 0x1bd66> (nil . bar)) (global (nil)))

;; get the frame-local value
(frame-value foo)
⇒ bar

;; get the default explicitly
(frame-get-default foo)
⇒ nil

;; Switch to another frame and evaluate:
;; C-x 5 o M-: (frame-setq foo 'baz) RET M-: (frame-value foo) RET
⇒ baz

;; Switch back.
;; C-x 5 o
(specifier-specs foo)
⇒ ((#<x-frame "emacs" 0x28ec> (nil . baz))
    (#<x-frame "Message" 0x1bd66> (nil . bar))
    (global (nil)))

(frame-value foo)
⇒ bar

(frame-get-default foo)
⇒ nil

Note that since specifiers generalize both frame-local and buffer-local variables in a sensible way, SXEmacs is not likely to put a high priority on implementing frame-local variables. In fact, some developers think that frame-local variables are evil for the same reason that buffer-local variables are evil: the declaration is both global and invisible. That is, you cannot tell whether a variable is “normal,” buffer-local, or frame-local just by looking at it. So if you have namespace management problems, and some other Lisp package happens to use a variable name that you already declared frame- or buffer-local, weird stuff happens, and it is extremely hard to track down.


Next: , Previous: , Up: Specifiers   [Contents][Index]