TypeScript constructor of symbolic expressions.
<1kB | no deps | tree-shakeable | side-effect free
This library provides a minimal yet powerful implementation of Lisp's symbolic expressions (s-expressions) for TypeScript. By starting with these fundamental building blocks, you can create immutable, composable data structures that work well with functional programming patterns. The library's simplicity makes it easy to understand and extend, while its type-safety ensures reliability in larger applications.
Deno
deno add jsr:@lambda/ts-expression
Node.js (npx)
npx jsr add @lambda/ts-expression
Bun
bunx jsr add @lambda/ts-expression
pnpm
pnpm dlx jsr add @lambda/ts-expression
Yarn
yarn dlx jsr add @lambda/ts-expression
import { car, cdr, cons } from "@lambda/ts-expression";
type Point = typeof makePoint;
const makePoint = (x: number, y: number) => cons(x, y);
const getX = (point: Point) => car(point);
const getY = (point: Point) => cdr(point);
const getSymmetricalPoint = (point: Point) => {
const x = getX(point);
const y = getY(point);
return makePoint(-x, -y);
};
const calculateDistance = (point1: Point, point2: Point) => {
const [x1, y1] = [getX(point1), getY(point1)];
const [x2, y2] = [getX(point2), getY(point2)];
return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
};
const point1 = makePoint(3, 4);
const point2 = makePoint(0, 0);
getX(point1); // 3
getY(point2); // 0
getSymmetricalPoint(makePoint(1, 5)); // makePoint(-1, -5)
calculateDistance(makePoint(-2, -3), makePoint(-4, 4)); // ≈ 7.28
import { cons, car, cdr } from "@lambda/ts-expression";
const leaf = <T>(value: T) => cons(value, cons(null, null));
const node = <N, L, R>(value: N, left: L, right: R) => {
return cons(value, cons(left, right));
};
8000
const tree = node(
"root",
node("left", leaf("left-left"), leaf("left-right")),
node("right", leaf("right-left"), leaf("right-right")),
);
car(tree); // "root"
car(car(cdr(tree))); // "left"
import { cons, car, cdr } from "@lambda/ts-expression";
const node = <A, B>(a: A, b: B) => cons(a, b);
const fs_tree = node(
"dir:root",
node(
node("dir:usr", node("dir:bin", node("dir:etc", "file:readme.txt"))),
node("dir:opt", node("dir:vol", node("dir:tmp", "file:script.sh"))),
),
);
const cdaar = car(car(cdr(fs_tree)));
const cdadar = car(cdr(car(cdr(fs_tree))));
const cdadddr = cdr(cdr(cdr(car(cdr(fs_tree)))));
const cddar = car(cdr(cdr(fs_tree)));
The general rule for c[a/d][a/d][a/d][a/d]r
functions:
- read from right to left (just like function composition)
a
stands for car (access the first element)d
stands for cdr (drop the first element)
Function | Equivalent Expression | Meaning |
---|---|---|
(car X) |
(first X) |
First element |
(cdr X) |
(rest X) |
Everything except the first element |
(cadr X) |
(car (cdr X)) |
Second element |
(cddr X) |
(cdr (cdr X)) |
Drops first two elements |
(caddr X) |
(car (cdr (cdr X))) |
Third element |
(cdddr X) |
(cdr (cdr (cdr X))) |
Drops first three elements |
Function | Equivalent Expression | Meaning |
---|---|---|
(caar X) |
(car (car X)) |
First of the first |
(cadr X) |
(car (cdr X)) |
Second element |
(cdar X) |
(cdr (car X)) |
Rest of the first |
(cddr X) |
(cdr (cdr X)) |
Drops first two elements |
Function | Equivalent Expression | Meaning |
---|---|---|
(caaar X) |
(car (car (car X))) |
First of the first of the first |
(caadr X) |
(car (car (cdr X))) |
First of the second |
(cadar X) |
(car (cdr (car X))) |
Second of the first |
(caddr X) |
(car (cdr (cdr X))) |
Third element |
(cdaar X) |
(cdr (car (car X))) |
Rest of the first of the first |
(cdadr X) |
(cdr (car (cdr X))) |
Rest of the second |
(cddar X) |
(cdr (cdr (car X))) |
Rest of the first after dropping its first element |
(cdddr X) |
(cdr (cdr (cdr X))) |
Drops first three elements |
This library provides a TypeScript implementation of symbolic expressions (S-expressions), which are a fundamental data structure in Lisp programming languages. S-expressions originated in the 1950s with John McCarthy's work on Lisp and have since become an elegant foundation for functional programming.
The core of S-expressions is the cons
cell - a simple pair that holds two values. This primitive structure can be used to build complex data structures like lists, trees, and graphs. The operations to access the parts of a cons cell are traditionally called:
car
(Contents of Address Register) - retrieves the first/left elementcdr
(Contents of Decrement Register) - retrieves the second/right element
These peculiar names are historical artifacts from the IBM 704 computer on which Lisp was first implemented.
Copyright © 2025 Roman Hnatiuk
Licensed under MIT.