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. Source code.

2. Data Types

Lisp-RPC supports the following data types:

2.1. 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).

2.2. 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")

3. 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.

3.1. Request Format

A request must be a Data type:

(data-name :param1 value1 :param2 value2)

Example:

(get-book :title "The Hobbit" :year 1937)

3.2. Response Format

The response is also a Data type:

(response-name :result value :status "success")

Example:

(book-info :id "B123" :available T)

3.3. Nested Data

Keywords can take Data, List, or Map types as values.

(update-user :id 1 :profile '(:email "[email protected]" :tags '("admin" "staff")))

4. Spec Mode

Spec Mode allows defining a formal schema for the RPC interface. This enables automatic library generation, type checking, and better error handling.

4.1. Defining a Package

(def-rpc-package package-name)

4.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

4.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)

5. Grammar and Validation Rules

  1. Keywords: All keys in Data and Map types must be Lisp keywords (starting with :).
  2. Quoting:
    • In Plain Mode: Lists and Maps MUST be quoted (e.g., '(...)).
    • In Spec Mode:
      • Types in def-msg are typically quoted symbols (e.g., 'string).
      • Anonymous maps in def-msg or def-rpc can be quoted or unquoted (depending on implementation, but consistent quoting is recommended).
      • Lists in def-msg use the (list 'type) constructor.
      • Optional types in def-msg 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-07-16 Thu 15:56

Validate