hy/docs/whyhy.rst
2020-03-11 10:08:49 +08:00

143 lines
6.1 KiB
ReStructuredText

=======
Why Hy?
=======
Hy is a multi-paradigm general-purpose programming language in the `Lisp family
<https://en.wikipedia.org/wiki/Lisp_(programming_language)>`_. It's implemented
as a kind of alternative syntax for Python. Compared to Python, Hy offers a
variety of extra features, generalizations, and syntactic simplifications, as
would be expected of a Lisp. Compared to other Lisps, Hy provides direct access
to Python's built-ins and third-party Python libraries, while allowing you to
freely mix imperative, functional, and object-oriented styles of programming.
Hy versus Python
----------------
The first thing a Python programmer will notice about Hy is that it has Lisp's
traditional parenthesis-heavy prefix syntax in place of Python's C-like infix
syntax. For example, ``print("The answer is", 2 + object.method(arg))`` could
be written ``(print "The answer is" (+ 2 (.method object arg)))`` in Hy.
Consequently, Hy is free-form: structure is indicated by parentheses rather
than whitespace, making it convenient for command-line use.
As in other Lisps, the value of a simplistic syntax is that it facilitates
Lisp's signature feature: `metaprogramming
<https://en.wikipedia.org/wiki/Metaprogramming>`_ through macros, which are
functions that manipulate code objects at compile time to produce new code
objects, which are then executed as if they had been part of the original code.
In fact, Hy allows arbitrary computation at compile-time. For example, here's a
simple macro that implements a C-style do-while loop, which executes its body
for as long as the condition is true, but at least once.
.. _do-while:
::
(defmacro do-while [condition &rest body]
`(do
~body
(while ~condition
~body)))
(setv x 0)
(do-while x
(print "This line is executed once."))
Hy also removes Python's restrictions on mixing expressions and statements,
allowing for more direct and functional code. For example, Python doesn't allow
:ref:`with <py:with>` blocks, which close a resource once you're done using it,
to return values. They can only execute a set of statements:
.. code-block:: python
with open("foo") as o:
f1 = o.read()
with open("bar") as o:
f2 = o.read()
print(len(f1) + len(f2))
In Hy, :ref:`with` returns the value of its last body form, so you can use it
like an ordinary function call::
(print (+
(len (with [o (open "foo")] (.read o))
(len (with [o (open "bar")] (.read o))))))
To be even more concise, you can put a ``with`` form in a :ref:`generator
expression <gfor>`::
(print (sum (gfor
filename ["foo" "bar"]
(len (with [o (open filename)] (.read o))))))
Finally, Hy offers several generalizations to Python's binary operators.
Operators can be given more than two arguments (e.g., ``(+ 1 2 3)``), including
augmented assignment operators (e.g., ``(+= x 1 2 3)``). They are also provided
as ordinary first-class functions of the same name, allowing them to be passed
to higher-order functions: ``(sum xs)`` could be written ``(reduce + xs)``.
The Hy compiler works by reading Hy source code into Hy model objects and
compiling the Hy model objects into Python abstract syntax tree (:py:mod:`ast`)
objects. Python AST objects can then be compiled and run by Python itself,
byte-compiled for faster execution later, or rendered into Python source code.
You can even :ref:`mix Python and Hy code in the same project, or even the same
file,<interop>` which can be a good way to get your feet wet in Hy.
Hy versus other Lisps
---------------------
At run-time, Hy is essentially Python code. Thus, while Hy's design owes a lot
to `Clojure <https://clojure.org>`_, it is more tightly coupled to Python than
Clojure is to Java; a better analogy is `CoffeeScript's
<https://coffeescript.org>`_ relationship to JavaScript. Python's built-in
:ref:`functions <py:built-in-funcs>` and :ref:`data structures
<py:bltin-types>` are directly available::
(print (int "deadbeef" :base 16)) ; 3735928559
(print (len [1 10 100])) ; 3
The same goes for third-party Python libraries from `PyPI <https://pypi.org>`_
and elsewhere. Here's a tiny `CherryPy <https://cherrypy.org>`_ web application
in Hy::
(import cherrypy)
(defclass HelloWorld []
#@(cherrypy.expose (defn index [self]
"Hello World!")))
(cherrypy.quickstart (HelloWorld))
You can even run Hy on `PyPy <https://pypy.org>`_ for a particularly speedy
Lisp.
Like all Lisps, Hy is `homoiconic
<https://en.wikipedia.org/wiki/Homoiconicity>`_. Its syntax is represented not
with cons cells or with Python's basic data structures, but with simple
subclasses of Python's basic data structures called :ref:`models <models>`.
Using models in place of plain ``list``\s, ``set``\s, and so on has two
purposes: models can keep track of their line and column numbers for the
benefit of error messages, and models can represent syntactic features that the
corresponding primitive type can't, such as the order in which elements appear
in a set literal. However, models can be concatenated and indexed just like
plain lists, and you can return ordinary Python types from a macro or give them
to ``eval`` and Hy will automatically promote them to models.
Hy takes much of its semantics from Python. For example, Hy is a Lisp-1 because
Python functions use the same namespace as objects that aren't functions. In
general, any Python code should be possible to literally translate to Hy. At
the same time, Hy goes to some lengths to allow you to do typical Lisp things
that aren't straightforward in Python. For example, Hy provides the
aforementioned mixing of statements and expressions, :ref:`name mangling
<mangling>` that transparently converts symbols with names like ``valid?`` to
Python-legal identifiers, and a :ref:`let` macro to provide block-level scoping
in place of Python's usual function-level scoping.
Overall, Hy, like Common Lisp, is intended to be an unopinionated big-tent
language that lets you do what you want. If you're interested in a more
small-and-beautiful approach to Lisp, in the style of Scheme, check out
`Hissp <https://github.com/gilch/hissp>`_, another Lisp embedded in Python
that was created by a Hy developer.