In Emacs, Everything Looks Like a Service
In Emacs, Everything Looks Like a Service
Notes based on the writing of Charles Choi (최민수) Date: 09 Jul 2026
It is a frequent claim that Emacs is an operating system (OS). While this is technically inaccurate, the comparison arises because Emacs possesses the unique ability to coordinate various utilities and applications sitting atop the OS kernel.
The following diagram illustrates the actual relationship between Emacs, the underlying OS, and its functional capabilities:
Because Emacs has native access to system services (such as the network and file system) and can execute external binaries, it is remarkably easy to build custom client behaviors directly within the editor. Consequently, users can satisfy a vast array of computing requirements through various "client modes," leading to the philosophy of “living exclusively in Emacs.”
This article explores how Emacs enables the construction of these clients, arguing that within this environment, everything is essentially a service.
Understanding the Client–Server Model
At its core, the client-server model is a design pattern that splits a task between two entities:
- The Service (Provider): The entity that holds the resource.
- The Client (Requester): The entity that asks for the resource.
Definition: A transaction consists of a request issued by the client and a subsequent response returned by the server.
These interactions can happen across a global network or locally on a single machine. The most prominent modern implementation of this is the REST-style architecture.
REST Interaction Flow
The sequence below depicts a standard RESTful communication pattern:
Emacs as a Client
To function as a client, Emacs must handle three primary concerns. We can view these as a checklist for implementation:
- UI (User Interface): How the data is presented to the user.
- Client Edge: The mechanism for communicating with the external service.
- Local Database: How the exchanged data is stored or synchronized locally.
Emacs provides a wealth of built-in and community libraries to satisfy these needs.
Available Emacs Tooling
| Concern | Emacs Libraries / Features |
|---|---|
| UI | Minibuffers, Buffers, Completion, Tabulated List Mode, Variable Pitch Table (vtable), Transient |
| Client Edge | URL, Socket (TCP/UDP), SMTP, JSON, XML |
| Local Database | Collections, Association Lists (alists), Property Lists (plists), Hash Tables, SQLite |
The level of complexity depends on the goal. If a command-line tool already exists to do the "heavy lifting," that tool can be treated as a service accessed via a shell call.
The Role of Elisp
All the aforementioned tools are orchestrated via Elisp (Emacs Lisp). Because Elisp is a dynamic programming language, it allows for extreme improvisation during runtime. This flexibility enables the complex coordination of everything from internal Lisp functions to external shell commands.
Practical Implementation: A wttr.in Client
To demonstrate, let's look at a client for wttr.in, a weather service designed for the console. Since it supports JSON, we can create a wttr command that prompts for a location, fetches the data, and displays it in the mini-buffer.
1. The Main Entry Point
The wttr function handles the user interaction and error catching.
(defun wttr (location)
"Show weather conditions for LOCATION from `https://wttr.in' in mini-buffer."
(interactive "sWhere (default: local): ")
(condition-case err
(let* ((url (wttr--request-url location))
(jsondb (fetch-json-as-hash-table url))
(msg (wttr--report-message jsondb)))
(kill-new msg)
(message "%s" msg))
(error
(message "ERROR: %s" (cdr err)))))
2. Constructing the Request
The URL must be formatted correctly to request JSON output (format=j1).
(defun wttr--request-url (location)
"Construct wttr.in URL with LOCATION."
(let* ((base-url (url-generic-parse-url "https://wttr.in"))
(encoded-location (string-replace "+" location))
(query (format "/%s?format=j1" encoded-location))
(_dummy (setf (url-filename base-url) query)))
(url-recreate-url base-url)))
3. Fetching and Parsing
This function handles the network request and converts the JSON response into an Elisp hash-table.
(defun fetch-json-as-hash-table (url)
"Fetch URL with expected JSON response and return a `hash-table'."
(let ((data-buffer (url-retrieve-synchronously url)))
(if (not data-buffer)
(error "Failed to fetch data from %s url" url)
(unwind-protect
(with-current-buffer data-buffer
;; Move point past the HTTP metadata headers
(goto-char (url-http-end-of-headers))
;; Parse the remaining JSON buffer into a hash-table
(json-parse-buffer :object-type 'hash-table))
;; Always kill the downloaded network buffer to prevent memory leaks
(kill-buffer data-buffer)))))
4. Formatting the Output
Finally, we extract the specific values. For example, the service provides temperatures in both Celsius and Fahrenheit, related by the formula:
(defun wttr--report-message (jsondb)
"Generate weather report message from JSONDB."
(let* ((area-buflist ())
(nearest-area (wttr--get-first jsondb 'nearest_area))
(area-name (map-elt (wttr--get-first nearest-area 'areaName) 'value))
(region (map-elt (wttr--get-first nearest-area 'region) 'value))
(country (map-elt (wttr--get-first nearest-area 'country) 'value))
(current-condition (wttr--get-first jsondb 'current_condition))
(temp_c (map-elt current-condition 'temp_C))
(temp_f (map-elt current-condition 'temp_F))
(weather-description (map-elt (wttr--get-first current-condition 'weatherDesc) 'value)))
(mapc (lambda (x)
(if (and x (not (string-equal x "")))
(push x area-buflist)))
(list area-name region country))
(format "%s: %s°C, %s°F %s"
(string-join area-buflist ", ")
temp_c temp_f weather-description)))
By leveraging these patterns, Emacs transforms from a mere text editor into a powerful orchestrator where any external data source or local utility is simply another service to be integrated.