Open-source project
hylang/hy avatar
hylang/hy

Hy: a Lisp dialect that compiles to Python AST, not to Python source

A dialect of Lisp that's embedded in Python

5,437 stars384 forksPythonNOASSERTION

At a glance

What is it?
Hy embeds Lisp syntax in Python by transforming its own forms directly into Python abstract syntax tree objects, so Python libraries stay importable and the interpreter stays CPython. The trade-off is a moving target: the package supports Python 3.9 through 3.15 only.
Who is it for?
Adopt Hy if you already write Python and want macros and s-expressions over the same libraries, or if you are teaching Lisp to people who need their existing Python code to keep working. Do not adopt it if you need a Lisp with its own runtime, its own numeric tower or a compiler that targets something other than CPython, and do not adopt it on a Python version outside the range setup.py declares.
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 64 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

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

Editorial analysis

The problem Hy targets: Python syntax, Lisp metaprogramming

Python gives you a fixed grammar. You can decorate functions, generate code with exec, or write a library that reads a config file, but you cannot add a new special form. Metaprogramming in Python happens at the level of objects and descriptors, not at the level of the language itself. Hy takes the other route: it keeps CPython as the runtime and replaces the surface syntax with s-expressions, so the reader, the macro expander and the compiler are all yours to extend.

The audience follows from that. Hy is for people who already know Python and want macros, or who know Lisp and want CPython's library ecosystem. The README states the design goal plainly: since Hy transforms its Lisp code into Python AST objects, the whole Python world stays at your fingertips, in Lisp form. That sentence is the whole pitch. It is not a new interpreter, not a new virtual machine, and not a language that happens to have Python bindings.

How Hy turns s-expressions into CPython AST objects

The pipeline is unusual in one respect: Hy does not emit Python source text and hand it to CPython. It builds Python AST nodes directly and compiles those. The practical consequence is that tracebacks, frames and the import system are CPython's, not a simulation of them.

Because the compiler is reachable from ordinary Python, Hy code and Python code interoperate in both directions. A .hy module can import a .py module, and a .py module can import a .hy one, provided the import hook is installed. The repository handles that at install time: setup.py defines a custom install command that, after the normal install, imports hy for its compile hooks and py_compile's every installed file ending in .hy, using CHECKED_HASH invalidation. That detail matters on read-only or container filesystems where bytecode cannot be written on first import.

The macro layer sits on the same footing as the rest of the language. Macros are ordinary Hy code that runs at compile time and returns forms, which is why the topics list on the repository includes macros and metaprogramming next to compiler. Nothing here is bolted on; the reader is extensible by construction.

Installing Hy and running a first program

The README gives one installation command. It installs into the user site directory, which avoids permission problems on managed systems:

bash
pip3 install --user hy

After that, two entry points are available. Running hy with no arguments starts an interactive read-eval-print loop, and passing a filename runs that program:

bash
hy myprogram.hy

The setup.py entry_points table also declares hyc and hy2py alongside hy. hyc compiles a Hy file, and hy2py translates Hy into Python source, which is the fastest way to understand what a macro actually expanded to. The README does not document either command's flags, so check the documentation at hylang.org/hy/doc before relying on specific options.

If you would rather not install anything locally, the README points to a web console at hylang.org/try-hy. The repository also ships a Dockerfile that installs the checkout in editable mode and sets the container command to hy, which is useful for trying the current master rather than a release.

The Python version window is the real constraint

setup.py declares python_requires=">= 3.9, < 3.16". That upper bound is not decorative. Because Hy builds AST nodes for a specific CPython version, a Python release that changes the AST surface can break the compiler until Hy catches up. If your platform ships a Python newer than the ceiling, Hy is simply not installable through the normal path, and no amount of macro cleverness fixes that.

The dependency surface is small: funcparserlib is pinned with a compatible-release specifier and is listed both as a setup requirement and an install requirement, because Hy files have to be compiled during setup. Small is not the same as absent, though. funcparserlib is a hard runtime dependency, so a fully vendored, zero-dependency deployment is not on the table.

The licence situation needs a caveat. The README says the licence is MIT (Expat), and setup.py carries license="Expat" with the MIT classifier, but the repository's licence metadata is reported as NOASSERTION. That is a metadata discrepancy rather than a contradiction, and it is worth resolving with whoever reviews dependencies in your organisation before you ship. Nothing here is legal advice.

Finally, Hy is the wrong tool when you want a Lisp that stands on its own. If you need a separate runtime, a different numeric tower, or a compiler targeting something other than CPython, embedding in Python is exactly the property you do not want.

Hy compared with writing macros in Python itself

The obvious alternative is plain Python with its own code-generation facilities: decorators, metaclasses, exec, and template libraries that emit source. The difference in approach is where the extension point lives. Python's facilities operate on objects and strings, so a generated function is a string you compile or an object you construct, and the compiler gives you no structured view of it. Hy's macros operate on forms before compilation, and the result is an AST the compiler already understands. That is a different kind of power: you can introduce syntax, not just behaviour.

The cost is the ecosystem boundary. Python tooling that parses source text, from linters to coverage reporters to editor plugins, sees .hy files as something else. Hy is a smaller community than Python, and the README directs questions to Stack Overflow under the [hy] tag or to GitHub Discussions, with the maintainer named as the person responsible for answering user questions. That is a normal arrangement for a project this size, but it means you are not going to find a Stack Overflow answer for every error message.

A second comparison is with other Lisp-to-something compilers. Hy's distinguishing choice is that it targets CPython's AST rather than emitting source or targeting a foreign runtime. That buys interop and costs portability.

Release cadence and what an upgrade costs you

The recent release history is steady: 1.2.0 in January 2026, 1.3.0 in May 2026, and 1.3.1 on 2026-07-31, which is also the date of the last push to the default branch. Releases carry names as well as numbers, and 1.3.1 is tagged 1.3.1 ("Eyes in the Sky"). The project is not archived, and the repository's Development Status classifier is 5 - Production/Stable.

Upgrade cost is dominated by the Python ceiling, not by Hy's own API churn. Every time your interpreter moves, check the declared range before you upgrade; if your platform jumps past the upper bound, you are pinned until Hy widens it. Beyond that, the NEWS.rst file at the repository root is the place to read before bumping, and the docs at hylang.org/hy/doc are the reference for anything the README omits, which is most things. The README itself is short and does not document rollback, version pinning strategy, or the flags of hyc and hy2py.

Editorial conclusion

Adopt Hy if you already write Python and want macros and s-expressions over the same libraries, or if you are teaching Lisp to people who need their existing Python code to keep working. Do not adopt it if you need a Lisp with its own runtime, its own numeric tower or a compiler that targets something other than CPython, and do not adopt it on a Python version outside the range setup.py declares. Before committing, check that your interpreter satisfies python_requires (>= 3.9, < 3.16), read the docs at hylang.org/hy/doc for the current macro API rather than trusting blog posts, and run hy2py over one real module of yours to see exactly what AST your code becomes.

Frequently asked questions

Can I use Lisp in Python?

Yes. Hy is a Lisp dialect embedded in Python, and the README states that it transforms its Lisp code into Python AST objects, so you keep the Python runtime and its libraries while writing s-expressions. Install it with pip3 install --user hy and start the REPL with hy.

How do I install Hy and run a Hy program?

The README gives pip3 install --user hy for the latest release. After that, run hy with no arguments for an interactive REPL, or hy myprogram.hy to execute a file.

Which Python versions does Hy support?

setup.py declares python_requires=">= 3.9, < 3.16", so the upper bound is part of the package metadata rather than a suggestion. A Python outside that range will not satisfy the requirement through the normal install path.

What are the hyc and hy2py commands for?

Both are declared in the entry_points table in setup.py alongside the hy command. hyc compiles a Hy file, and hy2py translates Hy into Python source, which is useful for seeing what a macro expanded to. The README does not document their flags.

What licence does Hy use?

The README says the licence is MIT (Expat), and setup.py carries license="Expat" with the MIT classifier, while the repository's licence metadata is reported as NOASSERTION. Treat that discrepancy as something to confirm before shipping.

Official sources

  1. hylang/hy on GitHub
  2. Issues
  3. Project website
  4. README
  5. 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/hylang-hy.svg)](https://hysenlabs.com/projects/hylang-hy)