Lisp-RPC Specification
Table of Contents
1. Introduction
Lisp-RPC is a lightweight, S-expression based Remote Procedure Call protocol. It supports two modes: Plain Mode for direct, dynamic communication and Spec Mode for schema-defined, type-checked communication.
2. Repositories
- lisp-rpc: Rust implementation
- lisp-rpc.cl: Common Lisp implementation
- lisp-rpc-json-convertor: JSON converter
- lisp-rpc-doc: Documentation and specification
3. Data Types
Lisp-RPC supports the following data types. For developers familiar with JSON, here is a quick mapping:
3.1. JSON vs. Lisp-RPC Quick Reference
| Concept | JSON Equivalent | Lisp-RPC Syntax | Notes |
|---|---|---|---|
| Named Data | {"data_name": "user", "id": 1} |
(user :id 1) |
Never quoted; used for messages and RPC calls |
| Anonymous Map | {"city": "Tokyo", "zip": 100} |
'(:city "Tokyo" :zip 100) |
Must be quoted |
| List / Sequence | ["apple", "banana"] |
'("apple" "banana") |
Must be quoted |
| Primitives | "hello" / 42 / 3.14 |
"hello" / 42 / 3.14 |
Strings, integers, floats |
| Boolean / Null | true / false / null |
T / NIL |
Case-insensitive |
| Enum / Symbol | "admin" |
'admin |
Quoted symbol |
3.2. Primary Types
- string: A standard Lisp string (e.g.,
"hello"). - number: Integers (e.g.,
42). - float: Floating-point numbers (e.g.,
3.14). - boolean: Represented by
T(true) andNIL(false). (Note: In S-expressions,NILalso represents an empty list). - symbol: A quoted identifier (e.g.,
'admin,'active). Used for enum variants and identifiers.
3.3. Complex Types
- Data: A named structure followed by keyword-value pairs.
- Format:
(data-name :key1 value1 :key2 value2 ...) - Example:
(book-info :title "1984" :id 1)
- Format:
- List: A sequence of elements. In Plain Mode, lists must be quoted.
- Format:
'(item1 item2 ...) - Example:
'(1 2 3)
- Format:
- Map (Anonymous Data): A collection of keyword-value pairs without a name. In Plain Mode, maps must be quoted.
- Format:
'(:key1 value1 :key2 value2 ...) - Example:
'(:first "John" :last "Doe")
- Format:
4. Plain Mode
In Plain Mode, the client sends a raw S-expression (as a string) to the server. The server evaluates or parses the call and returns a result in the same format.
4.1. Request Format
A request must be a Data type:
(data-name :param1 value1 :param2 value2)
Example:
(get-book :title "The Hobbit" :year 1937)
4.2. Response Format
The response is also a Data type:
(response-name :result value :status "success")
Example:
(book-info :id "B123" :available T)
4.3. Nested Data
Keywords can take Data, List, or Map types as values.
(update-user :id 1 :profile '(:email "[email protected]" :tags '("admin" "staff")))
Caution: Nested types (e.g. list in list, map in list) still require their own leading quotes:
;; List of lists: '('(1 2) '(3 4))
;; List of maps: '('(:id 1) '(:id 2))
(example-rpc :matrix '('(1 2) '(3 4)) :records '('(:id 1) '(:id 2)))
5. Spec Mode
Spec Mode allows defining a formal schema for the RPC interface. This enables automatic library generation, type checking, and better error handling.
Please check the example in code:
5.1. Defining a Package
(def-rpc-package package-name)
5.2. Defining Messages (Data Structures)
Use def-msg to define reusable data structures.
(def-msg message-name :key1 'type1 :key2 'type2 ...)
- Types can be primary types (
'string,'number/'int,'float,'boolean). - Types can be other defined messages.
- Types can be nested lists:
(list 'type). - Types can be nested maps:
'(:k1 'type1 :k2 'type2). Anonymous maps MUST be quoted. - Types can be optional:
(optional 'type).
Examples:
(def-msg language-preference :lang 'string)
(def-msg author-list :names (list 'string))
(def-msg user
:id 'number ;; or 'int, if it is float, use 'float
:middle-name (optional 'string)
:meta '(:created-at 'string :active 'boolean)) ;; Quoted map schema
5.3. Defining RPCs
Use def-rpc to define a remote procedure, specifying its input schema and output message type.
(def-rpc rpc-name
'(:param1 'type1 :param2 'type2 ...)
'response-message-name)
Example:
(def-rpc get-book
'(:title 'string :version (optional 'string)
:lang '(:lang 'string :encoding 'number)
:authors 'author-list)
'book-info)
6. Grammar and Validation Rules
- Keywords: All keys in Data and Map types must be Lisp keywords (starting with
:). - Quoting (Two Golden Rules):
- Rule 1: Named structures are NEVER quoted.
Top-level RPC requests, responses, and nested named Data structures do NOT take a quote (e.g.,
(get-book ...),(book-info ...)). - Rule 2: Literal data collections and symbols MUST be quoted.
Lists
'(...), Anonymous Maps'(:key val), Symbols'symbol, and nested collections (e.g., list of lists'('(1 2) '(3 4)), list of maps'('(:id 1) '(:id 2))) must each have a leading quote. - In Spec Mode:
- Types in
def-msganddef-rpcare quoted symbols (e.g.,'string, ='book-info). - Lists use the
(list 'type)constructor. - Optional types use the
(optional 'type)constructor.
- Types in
- Rule 1: Named structures are NEVER quoted.
Top-level RPC requests, responses, and nested named Data structures do NOT take a quote (e.g.,
- Uniqueness: Keyword names within a single Data or Map structure must be unique.
- Symbols: Use quoted symbols for type references to avoid evaluation errors in dynamic environments.