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

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) and NIL (false). (Note: In S-expressions, NIL also 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)
  • List: A sequence of elements. In Plain Mode, lists must be quoted.
    • Format: '(item1 item2 ...)
    • Example: '(1 2 3)
  • 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")

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

  1. Keywords: All keys in Data and Map types must be Lisp keywords (starting with :).
  2. 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-msg and def-rpc are quoted symbols (e.g., 'string, ='book-info).
      • Lists use the (list 'type) constructor.
      • Optional types use the (optional 'type) constructor.
  3. Uniqueness: Keyword names within a single Data or Map structure must be unique.
  4. Symbols: Use quoted symbols for type references to avoid evaluation errors in dynamic environments.

Author: ccQpein

Created: 2026-09-02 Wed 21:39

Validate