Homoiconic Python
Enthusiasm over a so‑called “homoiconic Python” quickly turns into a broader look at how Lisp’s features—homoiconicity, powerful macros, and REPL‑driven development—contrast with Python’s more restrictive, statement‑oriented design. Commenters highlight Lisp’s advanced debugging and error‑handling capabilities, such as interactive stack inspection and resumable exceptions, and share related projects like Hy, Clojure‑style Lisps on Python, and typed Lisps that aim to tame complexity. Others argue that while Lisp’s expressiveness enables elegant abstractions, it can also produce opaque, hard‑to‑maintain code, raising questions about how much flexibility mainstream languages should adopt.
What this project actually is
- Multiple commenters note the project is essentially “Lisp in Python,” not “homoiconic Python.”
- Some expected Python’s native syntax/AST to be directly manipulable; instead, it’s an interpreter for a Lisp-like language written in Python.
- Debate on homoiconicity:
- One view: it means the language is implemented in itself.
- Another: it means code is a first-class data structure that can be constructed, inspected, and evaluated at runtime, regardless of implementation language.
- Some doubt that truly homoiconic Python is possible without Python syntax being composed of native data structures evaluated directly.
Related languages and tools
- Suggestions for “Python-like scripting Lisps” or Lisp-on-Python:
- Hy, Basilisp (Clojure-style lisps targeting Python).
- Janet (fast, embeddable, Lua-style VM).
- Ciel (batteries-included Common Lisp “scripting” setup).
- Rhombus (Python-ish surface syntax with Racket-style macros).
- Other experiments: js-lisp (Lisp in JS), fakelisp (Lisp-like tuples in Python), yamlscript (“program in YAML”), Binary Lambda Calculus in Python, and a “homoiconic Java” DSL.
Lisp REPL, debugging, and error handling vs Python
- Several comments praise Common Lisp’s REPL-driven workflow and debugging:
- Ability to recompile and patch live code, inspect and modify stack frames, and resume execution via restarts.
- Conditions/restarts separate error detection, handler choice, and recovery strategy, often allowing interactive continuation.
- In contrast, Python typically unwinds the stack on exceptions; state at the throw site is lost after unwinding.
- Some point out Python can still break into debuggers at exceptions and inspect locals; tools like
py-spy,pystack,cpython-lldb, and periodic checkpointing (e.g., Joblib) are suggested. - Consensus: long-running Python jobs should persist intermediate state; relying on live introspection alone is risky.
Expressiveness, macros, and readability
- Many admire Lisps’ expressiveness, macros, and historical influence; others argue that this freedom often leads to unreadable “abominations.”
- One view: languages like Go or Python intentionally restrict expressiveness (no rich macros, clunky lambdas) to limit how “clever” or non-idiomatic code can become.
- Counterpoints:
- Any language can host terrible code; organization and developers matter more.
- Lisp style guides explicitly caution against overusing macros; they are powerful but should be written sparingly.
- Macros can improve readability by creating concise domain-specific layers (e.g., object systems) that hide lower-level complexity.
Types and “taming” Lisp
- For those worried about large Lisp codebases becoming hard to reason about:
- Typed Racket and Shen are offered as strongly-typed Lisps.
- Dylan and Common Lisp (with compiler type declarations and warnings, especially in SBCL) are mentioned as Lisps with useful type systems or type checking support.