Library / SDK
naver/lispe avatar
naver/lispe

LispE is a Lisp that grew array operators, pattern matching and threads

An implementation of a full fledged Lisp interpreter with Data Structure, Pattern Programming and High level Functions with Lazy Evaluation à la Haskell.

607 stars19 forksCNOASSERTION

At a glance

What is it?
naver/lispe is a C implementation of a Lisp dialect whose goal is to harness the power of functional languages with array languages, with built in pattern matching, data structures, classes, thread spaces and APL style array operators, plus bindings to libtorch, llama.cpp, MLX and tiktoken. It compiles to a native binary, to WebAssembly for the browser, and it is licensed under a custom licence that GitHub could not classify.
Who is it for?
Adopt LispE if you want a Lisp that treats vectors as first class values, gives you pattern matching with guards instead of nested conditionals, and runs on the CPU, in a browser through WebAssembly, or next to PyTorch and llama.cpp models.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The goal is one language for functional and array work

LispE, short for Lisp Elémentaire, is described as a version of Lisp that is both compact and offers a large variety of functional and array language features. The stated main goal is to provide a multi-platform language that can harness the power of functional languages with array languages, and the reasoning behind it is that the strength of Lisp is its very simple but pretty versatile formalism, which helps combine those programming trends in one single language. The intellectual debt is declared too, since a large part of the work is based on Paul Graham's article on the root of Lisp. It is a NAVER project, and the code comes with a small internal editor taken from another NAVER project, TAMGU. The language description, an index of the available functions and a help page for functions, operators and libraries all live in the project wiki, so the repository itself is a signpost rather than the manual.

The core is a Lisp with the usual operators

The first sample shows the traditional operators doing their traditional work:

lisp
(cons 'a '(b c)) ; (a b c)
(cdr '(a b c d e)) ; '(b c d e)
(car '(a b c d e)) ; 'a

(+ 10 20 (* 3 4)) ; 42

(list 'a 'b 'c 'd 'e) ; (a b c d e)

Then the parts that are less obvious. The language has built in types for numbers and strings, and the addition operator is overloaded for them, so that two lists of integers and two lists of strings are added element by element rather than concatenated:

lisp
(setq l1 (integers 1 2 3))
(setq l2 (integers 4 5 6))

; We can then add these lists together
(+ l1 l2) ; 5 7 9

; or strings

(setq s1 (strings "a" "b" "c"))
(setq s2 (strings "d" "e" "f"))

; We can then add these lists together
(+ s1 s2) ; ("ad" "be" "cf")

Lists are treated as vectors, so the indexing operator works on them, and there is a loop construct for iteration over a list.

Pattern matching replaces the usual conditionals

The feature that goes furthest beyond a traditional Lisp is the built in pattern matching mechanism, and the fizzbuzz example in the README shows how it is used. A pattern definition can call a function inside the pattern, so the argument is checked by a predicate and then bound:

lisp
(defun checking (x y) (eq 0 (% y x)))

; the argument should be an integer, whose value is a multiple of 15
; The argument is stored in x
(defpat fizzbuzz ((integer_ (checking 15 x))) 'fizzbuzz)
(defpat fizzbuzz ((integer_ (checking 3 x))) 'fizz)
(defpat fizzbuzz ((integer_ (checking 5 x))) 'buzz)
(defpat fizzbuzz (x) x)
(mapcar 'fizzbuzz (range 1 100 1))

The same mechanism is what makes the data structure feature work. A data@ declaration defines a set of constructors, and pattern functions then match on them, so a Surface function can compute the area of a circle or a rectangle without a chain of type tests:

lisp
(data@ (Point integer_ integer_) (Pixel _ _) (Circle (Point _ _)  _) (Rectangle (Point _ _)  _ nil))

; Then some pattern methods
(defpat Surface ((Circle (Point x y) r)) (* _pi r r))
(defpat Surface ((Rectangle _ h w)) (* h w))
(defpat Surface (true) 'wrong)

The catch-all pattern, true, is what catches everything that did not match, which is how a default return value is expressed.

There are classes, and the syntax is its own thing

LispE also has some object oriented programming, in the form of classes. The declaration looks like this:

lisp
; Defines a class named 'truc' with fields x, y, and z
(class@ truc (x y z)

   (defun appel(u)
      (setq x 100)
      (println x y z)
      (+ x y z u)
   )
)

A second class shows two features that are worth noting. A field can have a default value, as in (class@ machin (x y (z 1))), and a method can add a field to the class at run time, with setqi setting a field on the instance and a new field v being added in the example. None of this is inherited from Common Lisp, and the README links a wiki page on classes, which is where the full rules live. The point for an evaluator is that LispE is a dialect with its own conventions rather than a wrapper around an existing implementation, so a Common Lisp habit will mostly transfer and a Common Lisp assumption about standard macros will not.

Threads get their own namespace

Threading is presented as easy to run, and the mechanism is a dedicated space per thread. The example creates a threadspace, and the README notes that variables in threadspace are protected against value concurrencies:

lisp
(threadspace
   (seth titi 10)
   (seth toto (rho 2 10 '(0)))
)

A thread is then declared with the dethread keyword, launched in a loop, and waited for, after which the values can be read back from the space, with the README pointing out that the order of values is random because of thread execution. The design decision here is to give each thread its own namespace rather than to rely on locking around shared variables, and the name space is an ordinary space, since the example shows that threadspace is actually a specific name space. For a language that also ships async/ at the top level of the repository, that is a coherent story, but it does mean the concurrency model is not the one a Common Lisp or a Python developer would reach for first.

The array side is APL flavoured, and it is not decorative

Thanks to an internal structure implemented with arrays, LispE provides array operators, and the Conway game of life example in the README shows what that means in practice. The life of life is solved in a single instruction, with a macro for the rotation operator and the whole computation written in the array notation:

lisp
(defmacro ⊖(l n) (rotate l n true))

(defun lifeinit(x y) (rho x y . random_choice (* x y) (integers 0 1) 17))

(setq r (lifeinit 20 40))
(defun gol8(⍵) ((λ(⍺) (| (& (== ⍺ 4) r) (== ⍺ 3))) (⌿ '+ (⌿  '+ (° (λ (x ⍺) (⊖ ⍺ x)) '(1 0 -1) (↑ (λ (x) (⌽ ⍵ x)) '(1 0 -1)))))))

Reading that takes practice, because the symbols are APL derived, with lambda written as λ, the argument placeholder as ⍺, the function placeholder as ⍵, and operators such as ⌿ for reduction, ↑ for take, ⌽ for reverse and ° for composition. The README links a wiki page on array operators, described as A la APL, and the motivation is that array code is written once and applied to whole vectors rather than element by element. That is the reason to look at LispE rather than at a plain Lisp, and it is also the reason it is not a drop-in for anything else.

It runs in a browser and it binds to AI runtimes

LispE is also a WASM library, so you can test it in a browser at the project page, and the README points to a wiki page on a predicate based agent in the browser, PREDIBAG. Precompiled binaries are stashed in the repository for Windows and Mac OS, including M1. The more distinctive part is the set of AI library bindings, each in its own directory. lispe_torch encapsulates the PyTorch internal libtorch library and SentencePiece. lispe_gguf is based on llama.cpp and loads and runs gguf models. lispe_mlx is based on the MLX library to access Mac GPUs. lispe_tiktoken is based on the tiktoken library by Open AI for tokenization. For a Lisp with a lazy evaluation story, that is a coherent direction: a language with a compact evaluator is a plausible scripting layer for models, and the bindings are the evidence that somebody is doing exactly that.

Building it is a C++11 make, and the tree is broad

The build is a plain makefile that compiles the interpreter sources into objs/ and links them into a binary called lispe, with a separate target for a smaller executable called jag:

make
lispe: $(OBJECTCXX) $(OBJECTMAIN)
	$(COMPPLUSPLUS) -o bin/lispe $(OBJECTCXX) $(OBJECTMAIN) -ldl -lpthread $(LIBBOOST)

The object rule is unremarkable and the comment above it notes that the compiler flags may need to be chosen per compiler, with commented out alternatives for the older standard names. The repository is where the breadth shows, with directories for the editor, the docker and wasm builds, examples, a Pythonic/ directory, and standalone library areas including async/, blas/, curl/, sqlite/, transducer/, xml/, gui/ and the AI binding directories. The examples directory is the best guide to the language, with an AdventOfCode2021, AdventOfCode2023 and AdventOfCode2024 set alongside haskillish, ollama, patterns, python_interpreter, sockets, threads and transpiler folders. The licence is the one point to check before you build on it, since GitHub could not classify it and the repository carries both a LICENSE and a NOTICE file.

Editorial conclusion

Adopt LispE if you want a Lisp that treats vectors as first class values, gives you pattern matching with guards instead of nested conditionals, and runs on the CPU, in a browser through WebAssembly, or next to PyTorch and llama.cpp models. Do not adopt it for production work without reading the custom licence and the NOTICE file, since the repository licence is one GitHub could not classify, and do not expect the language to be a drop-in for a Common Lisp implementation, because the syntax is its own. Before you commit, build the interpreter from the Makefile on your platform, check the prebuilt binaries if you are on Windows or macOS, and read the wiki pages for the pattern and array operator semantics.

Frequently asked questions

What is LispE?

It is a C implementation of a Lisp dialect, Lisp Elémentaire, whose stated goal is to provide a multi-platform language that harnesses the power of functional languages with array languages. The code also ships with a small internal editor taken from another NAVER project, TAMGU.

How does pattern matching work in LispE?

Patterns are defined with defpat, and a pattern can call a function inside the definition, so the argument is tested by a predicate and then bound. The README shows fizzbuzz written that way, with separate patterns for multiples of 15, 3 and 5, a catch-all, and a mapcar over a range.

Can I run LispE in a browser?

Yes. Since LispE is also a WASM library, the README points to a browser page where you can test it, and to a wiki page on a predicate based agent in the browser called PREDIBAG. Precompiled binaries for Windows and Mac OS, including M1, are stashed in the repository as well.

Which AI libraries does LispE bind to?

lispe_torch encapsulates the PyTorch internal libtorch library and SentencePiece, lispe_gguf is based on llama.cpp and loads and runs gguf models, lispe_mlx uses the MLX library to access Mac GPUs, and lispe_tiktoken is based on the tiktoken library by Open AI for tokenization.

How do I build the LispE interpreter?

The repository ships a makefile that compiles the interpreter sources into objs/ and links a lispe binary, and it is generated from Makefile.in with configure.py. There are also separate targets for a smaller jag executable, and directories for docker and wasm builds, with the supported platform list including Windows and Mac OS binaries.

Official sources

  1. Issues
  2. naver/lispe on GitHub
  3. README
  4. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/naver-lispe.svg)](https://hysenlabs.com/projects/naver-lispe)

Was this useful?