Merge pull request #1811 from Kodiologist/intro-docs-improvements
Overhaul introductory documentation
This commit is contained in:
commit
645d2e0b8e
39
README.md
39
README.md
@ -1,38 +1,22 @@
|
|||||||
Hy
|
Hy
|
||||||
==
|
==
|
||||||
|
|
||||||
[![Build Status](https://img.shields.io/travis/hylang/hy/master.svg)](https://travis-ci.org/hylang/hy)
|
|
||||||
[![Version](https://img.shields.io/pypi/v/hy.svg)](https://pypi.python.org/pypi/hy)
|
[![Version](https://img.shields.io/pypi/v/hy.svg)](https://pypi.python.org/pypi/hy)
|
||||||
|
|
||||||
<a href="https://xkcd.com/224/"><img title="We lost the documentation on quantum mechanics. You'll have to decode the regexes yourself." alt="XKCD #224" src="https://raw.github.com/hylang/shyte/18f6925e08684b0e1f52b2cc2c803989cd62cd91/imgs/xkcd.png"></a>
|
<a href="https://xkcd.com/224/"><img title="We lost the documentation on quantum mechanics. You'll have to decode the regexes yourself." alt="XKCD #224" src="https://raw.github.com/hylang/shyte/18f6925e08684b0e1f52b2cc2c803989cd62cd91/imgs/xkcd.png"></a>
|
||||||
|
|
||||||
Lisp and Python should love each other. Let's make it happen. [Try it](http://try-hy.appspot.com/).
|
Lisp and Python should love each other. Let's make it happen.
|
||||||
|
|
||||||
Hylarious Hacks
|
Hy is a Lisp dialect that's embedded in Python. Since Hy transforms its Lisp
|
||||||
---------------
|
code into Python abstract syntax tree (AST) objects, you have the whole
|
||||||
|
beautiful world of Python at your fingertips, in Lisp form.
|
||||||
|
|
||||||
* [Django + Lisp](https://github.com/paultag/djlisp/tree/master/djlisp)
|
To install the latest stable release of Hy, just use the command `pip3 install
|
||||||
* [Python `sh` Fun](https://twitter.com/paultag/status/314925996442796032)
|
--user hy`. Then you can start an interactive read-eval-print loop (REPL) with
|
||||||
* [Hy IRC Bot](https://github.com/hylang/hygdrop)
|
the command `hy`, or run a Hy program with `hy myprogram.hy`.
|
||||||
* [miniKanren in Hy](https://github.com/algernon/adderall)
|
|
||||||
|
|
||||||
OK, so, why?
|
* [Why Hy?](http://docs.hylang.org/en/master/whyhy.html)
|
||||||
------------
|
* [Tutorial](http://docs.hylang.org/en/master/tutorial.html)
|
||||||
|
|
||||||
Well. Python is awesome. So awesome, that we have so many tools to alter the
|
|
||||||
language in a *core* way, but we never use them.
|
|
||||||
|
|
||||||
Why?
|
|
||||||
|
|
||||||
Well, I wrote Hy to help people realize one thing about Python:
|
|
||||||
|
|
||||||
It's really awesome.
|
|
||||||
|
|
||||||
Oh, and lisps are neat.
|
|
||||||
|
|
||||||
![Cuddles the Hacker](https://i.imgur.com/QbPMXTN.png)
|
|
||||||
|
|
||||||
(fan art from the one and only [doctormo](http://doctormo.deviantart.com/art/Cuddles-the-Hacker-372184766))
|
|
||||||
|
|
||||||
Project
|
Project
|
||||||
-------
|
-------
|
||||||
@ -41,10 +25,13 @@ Project
|
|||||||
* Documentation:
|
* Documentation:
|
||||||
* stable, for use with the latest stable release: http://hylang.org/
|
* stable, for use with the latest stable release: http://hylang.org/
|
||||||
* master, for use with the latest revision on GitHub: http://docs.hylang.org/en/master
|
* master, for use with the latest revision on GitHub: http://docs.hylang.org/en/master
|
||||||
* Quickstart: http://hylang.org/en/stable/quickstart.html
|
|
||||||
* Bug reports: We have no bugs! Your bugs are your own! (https://github.com/hylang/hy/issues)
|
* Bug reports: We have no bugs! Your bugs are your own! (https://github.com/hylang/hy/issues)
|
||||||
* License: MIT (Expat)
|
* License: MIT (Expat)
|
||||||
* [Hacking on Hy](http://docs.hylang.org/en/master/hacking.html)
|
* [Hacking on Hy](http://docs.hylang.org/en/master/hacking.html)
|
||||||
* [Contributor Guidelines](http://docs.hylang.org/en/master/hacking.html#contributor-guidelines)
|
* [Contributor Guidelines](http://docs.hylang.org/en/master/hacking.html#contributor-guidelines)
|
||||||
* [Code of Conduct](http://docs.hylang.org/en/master/hacking.html#contributor-code-of-conduct)
|
* [Code of Conduct](http://docs.hylang.org/en/master/hacking.html#contributor-code-of-conduct)
|
||||||
* IRC: Join #hy on [freenode](https://webchat.freenode.net/)
|
* IRC: Join #hy on [freenode](https://webchat.freenode.net/)
|
||||||
|
|
||||||
|
![Cuddles the Hacker](https://i.imgur.com/QbPMXTN.png)
|
||||||
|
|
||||||
|
(fan art from the one and only [doctormo](http://doctormo.deviantart.com/art/Cuddles-the-Hacker-372184766))
|
||||||
|
@ -206,6 +206,8 @@ Recursively performs all possible macroexpansions in form, using the ``require``
|
|||||||
Macros
|
Macros
|
||||||
======
|
======
|
||||||
|
|
||||||
|
.. _let:
|
||||||
|
|
||||||
let
|
let
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
.. _hacking:
|
||||||
|
|
||||||
===============
|
===============
|
||||||
Hacking on Hy
|
Hacking on Hy
|
||||||
===============
|
===============
|
||||||
|
@ -1,36 +1,27 @@
|
|||||||
Welcome to Hy's documentation!
|
The Hy Manual
|
||||||
==============================
|
=============
|
||||||
|
|
||||||
.. image:: _static/hy-logo-small.png
|
.. image:: _static/hy-logo-small.png
|
||||||
:alt: Hy
|
:alt: Hy
|
||||||
:align: left
|
:align: left
|
||||||
|
|
||||||
:Try Hy: https://try-hy.appspot.com
|
|
||||||
:PyPI: https://pypi.python.org/pypi/hy
|
:PyPI: https://pypi.python.org/pypi/hy
|
||||||
:Source: https://github.com/hylang/hy
|
:Source: https://github.com/hylang/hy
|
||||||
:List: `hylang-discuss <https://groups.google.com/forum/#!forum/hylang-discuss>`_
|
:List: `hylang-discuss <https://groups.google.com/forum/#!forum/hylang-discuss>`_
|
||||||
:IRC: ``#hy`` on Freenode
|
:IRC: irc://chat.freenode.net/hy
|
||||||
:Build status:
|
|
||||||
.. image:: https://secure.travis-ci.org/hylang/hy.png
|
|
||||||
:alt: Travis CI
|
|
||||||
:target: http://travis-ci.org/hylang/hy
|
|
||||||
|
|
||||||
Hy is a wonderful dialect of Lisp that's embedded in Python.
|
Hy is a Lisp dialect that's embedded in Python. Since Hy transforms its Lisp
|
||||||
|
code into Python abstract syntax tree (AST) objects, you have the whole
|
||||||
|
beautiful world of Python at your fingertips, in Lisp form.
|
||||||
|
|
||||||
Since Hy transforms its Lisp code into the Python Abstract Syntax
|
To install the latest stable release of Hy, just use the command ``pip3 install
|
||||||
Tree, you have the whole beautiful world of Python at your fingertips,
|
--user hy``. Then you can start an interactive read-eval-print loop (REPL) with
|
||||||
in Lisp form!
|
the command ``hy``, or run a Hy program with ``hy myprogram.hy``.
|
||||||
|
|
||||||
|
|
||||||
Documentation Index
|
|
||||||
===================
|
|
||||||
|
|
||||||
Contents:
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 3
|
:maxdepth: 3
|
||||||
|
|
||||||
quickstart
|
whyhy
|
||||||
tutorial
|
tutorial
|
||||||
style-guide
|
style-guide
|
||||||
language/index
|
language/index
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
.. _special-forms:
|
||||||
|
|
||||||
=================
|
=================
|
||||||
Built-Ins
|
Built-Ins
|
||||||
=================
|
=================
|
||||||
@ -279,6 +281,8 @@ This is completely discarded and doesn't expand to anything, not even ``None``.
|
|||||||
Hy
|
Hy
|
||||||
|
|
||||||
|
|
||||||
|
.. _cond:
|
||||||
|
|
||||||
cond
|
cond
|
||||||
----
|
----
|
||||||
|
|
||||||
@ -338,6 +342,8 @@ is only called on every other value in the list.
|
|||||||
(side-effect2 x))
|
(side-effect2 x))
|
||||||
|
|
||||||
|
|
||||||
|
.. _do:
|
||||||
|
|
||||||
do
|
do
|
||||||
----------
|
----------
|
||||||
|
|
||||||
@ -400,6 +406,8 @@ the second of which becomes each value.
|
|||||||
{0: 0, 1: 10, 2: 20, 3: 30, 4: 40}
|
{0: 0, 1: 10, 2: 20, 3: 30, 4: 40}
|
||||||
|
|
||||||
|
|
||||||
|
.. _setv:
|
||||||
|
|
||||||
setv
|
setv
|
||||||
----
|
----
|
||||||
|
|
||||||
@ -416,20 +424,19 @@ For example:
|
|||||||
=> (counter [1 2 3 4 5 2 3] 2)
|
=> (counter [1 2 3 4 5 2 3] 2)
|
||||||
2
|
2
|
||||||
|
|
||||||
They can be used to assign multiple variables at once:
|
You can provide more than one target–value pair, and the assignments will be made in order::
|
||||||
|
|
||||||
.. code-block:: hy
|
(setv x 1 y x x 2)
|
||||||
|
(print x y) ; => 2 1
|
||||||
|
|
||||||
=> (setv a 1 b 2)
|
You can perform parallel assignments or unpack the source value with square brackets and :ref:`unpack-iterable`::
|
||||||
(1L, 2L)
|
|
||||||
=> a
|
|
||||||
1L
|
|
||||||
=> b
|
|
||||||
2L
|
|
||||||
=>
|
|
||||||
|
|
||||||
|
(setv duo ["tim" "eric"])
|
||||||
|
(setv [guy1 guy2] duo)
|
||||||
|
(print guy1 guy2) ; => tim eric
|
||||||
|
|
||||||
``setv`` always returns ``None``.
|
(setv [letter1 letter2 #* others] "abcdefg")
|
||||||
|
(print letter1 letter2 others) ; => a b ['c', 'd', 'e', 'f', 'g']
|
||||||
|
|
||||||
|
|
||||||
setx
|
setx
|
||||||
@ -444,6 +451,8 @@ Whereas ``setv`` creates an assignment statement, ``setx`` creates an assignment
|
|||||||
3 is greater than 0
|
3 is greater than 0
|
||||||
|
|
||||||
|
|
||||||
|
.. _defclass:
|
||||||
|
|
||||||
defclass
|
defclass
|
||||||
--------
|
--------
|
||||||
|
|
||||||
@ -914,6 +923,9 @@ raising an exception.
|
|||||||
=> (first [])
|
=> (first [])
|
||||||
None
|
None
|
||||||
|
|
||||||
|
|
||||||
|
.. _for:
|
||||||
|
|
||||||
for
|
for
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -992,6 +1004,8 @@ written without accidental variable name clashes.
|
|||||||
|
|
||||||
Section :ref:`using-gensym`
|
Section :ref:`using-gensym`
|
||||||
|
|
||||||
|
.. _get:
|
||||||
|
|
||||||
get
|
get
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -1022,6 +1036,8 @@ successive elements in a nested structure. Example usage:
|
|||||||
index that is out of bounds.
|
index that is out of bounds.
|
||||||
|
|
||||||
|
|
||||||
|
.. _gfor:
|
||||||
|
|
||||||
gfor
|
gfor
|
||||||
----
|
----
|
||||||
|
|
||||||
@ -1063,6 +1079,8 @@ keyword, the second function would have raised a ``NameError``.
|
|||||||
(set-a 5)
|
(set-a 5)
|
||||||
(print-a)
|
(print-a)
|
||||||
|
|
||||||
|
.. _if:
|
||||||
|
|
||||||
if / if* / if-not
|
if / if* / if-not
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
@ -1188,6 +1206,8 @@ that ``import`` can be used.
|
|||||||
(import [sys [*]])
|
(import [sys [*]])
|
||||||
|
|
||||||
|
|
||||||
|
.. _fn:
|
||||||
|
|
||||||
fn
|
fn
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
@ -1253,6 +1273,8 @@ last
|
|||||||
6
|
6
|
||||||
|
|
||||||
|
|
||||||
|
.. _lfor:
|
||||||
|
|
||||||
lfor
|
lfor
|
||||||
----
|
----
|
||||||
|
|
||||||
@ -1396,6 +1418,8 @@ print
|
|||||||
.. note:: ``print`` always returns ``None``.
|
.. note:: ``print`` always returns ``None``.
|
||||||
|
|
||||||
|
|
||||||
|
.. _quasiquote:
|
||||||
|
|
||||||
quasiquote
|
quasiquote
|
||||||
----------
|
----------
|
||||||
|
|
||||||
@ -1414,6 +1438,8 @@ using ``unquote`` (``~``). The evaluated form can also be spliced using
|
|||||||
; equivalent to '(foo bar baz)
|
; equivalent to '(foo bar baz)
|
||||||
|
|
||||||
|
|
||||||
|
.. _quote:
|
||||||
|
|
||||||
quote
|
quote
|
||||||
-----
|
-----
|
||||||
|
|
||||||
@ -1431,6 +1457,8 @@ alternatively be written using the apostrophe (``'``) symbol.
|
|||||||
Hello World
|
Hello World
|
||||||
|
|
||||||
|
|
||||||
|
.. _require:
|
||||||
|
|
||||||
require
|
require
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@ -1571,6 +1599,8 @@ sfor
|
|||||||
equivalent to ``(set (lfor CLAUSES VALUE))``. See `lfor`_.
|
equivalent to ``(set (lfor CLAUSES VALUE))``. See `lfor`_.
|
||||||
|
|
||||||
|
|
||||||
|
.. _cut:
|
||||||
|
|
||||||
cut
|
cut
|
||||||
-----
|
-----
|
||||||
|
|
||||||
@ -1677,6 +1707,8 @@ the given conditional is ``False``. The following shows the expansion of this ma
|
|||||||
(do statement))
|
(do statement))
|
||||||
|
|
||||||
|
|
||||||
|
.. _unpack-iterable:
|
||||||
|
|
||||||
unpack-iterable, unpack-mapping
|
unpack-iterable, unpack-mapping
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
@ -1717,6 +1749,8 @@ more than once in one expression (:pep:`3132`, :pep:`448`).
|
|||||||
[1, 2, 3, 4]
|
[1, 2, 3, 4]
|
||||||
|
|
||||||
|
|
||||||
|
.. _unquote:
|
||||||
|
|
||||||
unquote
|
unquote
|
||||||
-------
|
-------
|
||||||
|
|
||||||
@ -1792,6 +1826,8 @@ following shows the expansion of the macro.
|
|||||||
(if conditional (do statement))
|
(if conditional (do statement))
|
||||||
|
|
||||||
|
|
||||||
|
.. _while:
|
||||||
|
|
||||||
while
|
while
|
||||||
-----
|
-----
|
||||||
|
|
||||||
@ -1851,6 +1887,9 @@ prints
|
|||||||
In condition
|
In condition
|
||||||
At end of outer loop
|
At end of outer loop
|
||||||
|
|
||||||
|
|
||||||
|
.. _with:
|
||||||
|
|
||||||
with
|
with
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
.. _interop:
|
||||||
|
|
||||||
=====================
|
=====================
|
||||||
Hy <-> Python interop
|
Hy <-> Python interop
|
||||||
=====================
|
=====================
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
.. _syntax:
|
||||||
|
|
||||||
==============
|
==============
|
||||||
Syntax
|
Syntax
|
||||||
==============
|
==============
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
==========
|
|
||||||
Quickstart
|
|
||||||
==========
|
|
||||||
|
|
||||||
.. image:: _static/cuddles-transparent-small.png
|
|
||||||
:alt: Karen Rustard's Cuddles
|
|
||||||
|
|
||||||
(Thanks to Karen Rustad for Cuddles!)
|
|
||||||
|
|
||||||
|
|
||||||
**HOW TO GET HY REAL FAST**:
|
|
||||||
|
|
||||||
1. Create a `Virtual Python Environment
|
|
||||||
<https://pypi.python.org/pypi/virtualenv>`_.
|
|
||||||
2. Activate your Virtual Python Environment.
|
|
||||||
3. Install `hy from GitHub <https://github.com/hylang/hy>`_ with ``$ pip install git+https://github.com/hylang/hy.git``.
|
|
||||||
4. Start a REPL with ``hy``.
|
|
||||||
5. Type stuff in the REPL::
|
|
||||||
|
|
||||||
=> (print "Hy!")
|
|
||||||
Hy!
|
|
||||||
=> (defn salutationsnm [name] (print (+ "Hy " name "!")))
|
|
||||||
=> (salutationsnm "YourName")
|
|
||||||
Hy YourName!
|
|
||||||
|
|
||||||
etc
|
|
||||||
|
|
||||||
6. Hit CTRL-D when you're done.
|
|
||||||
7. If you're familiar with Python, start the REPL using ``hy --spy`` to check what happens inside::
|
|
||||||
|
|
||||||
=> (+ "Hyllo " "World" "!")
|
|
||||||
'Hyllo ' + 'World' + '!'
|
|
||||||
|
|
||||||
'Hyllo World!'
|
|
||||||
|
|
||||||
*OMG! That's amazing! I want to write a Hy program.*
|
|
||||||
|
|
||||||
8. Open up an elite programming editor and type::
|
|
||||||
|
|
||||||
#! /usr/bin/env hy
|
|
||||||
(print "I was going to code in Python syntax, but then I got Hy.")
|
|
||||||
|
|
||||||
9. Save as ``awesome.hy``.
|
|
||||||
10. Make it executable::
|
|
||||||
|
|
||||||
chmod +x awesome.hy
|
|
||||||
|
|
||||||
11. And run your first Hy program::
|
|
||||||
|
|
||||||
./awesome.hy
|
|
||||||
|
|
||||||
12. Take a deep breath so as to not hyperventilate.
|
|
||||||
13. Smile villainously and sneak off to your hydeaway and do
|
|
||||||
unspeakable things.
|
|
@ -2,272 +2,174 @@
|
|||||||
Tutorial
|
Tutorial
|
||||||
========
|
========
|
||||||
|
|
||||||
.. TODO
|
.. image:: _static/cuddles-transparent-small.png
|
||||||
..
|
:alt: Karen Rustard's Cuddles
|
||||||
.. - How do I index into arrays or dictionaries?
|
|
||||||
.. - Blow your mind with macros!
|
|
||||||
.. - Where's my banana???
|
|
||||||
|
|
||||||
Welcome to the Hy tutorial!
|
This chapter provides a quick introduction to Hy. It assumes a basic background
|
||||||
|
in programming, but no specific prior knowledge of Python or Lisp.
|
||||||
|
|
||||||
In a nutshell, Hy is a Lisp dialect, but one that converts its
|
Lisp-stick on a Python
|
||||||
structure into Python ... literally a conversion into Python's abstract
|
======================
|
||||||
syntax tree! (Or to put it in more crude terms, Hy is lisp-stick on a
|
|
||||||
Python!)
|
|
||||||
|
|
||||||
This is pretty cool because it means Hy is several things:
|
Let's start with the classic::
|
||||||
|
|
||||||
- A Lisp that feels very Pythonic
|
(print "Hy, world!")
|
||||||
- For Lispers, a great way to use Lisp's crazy powers but in the wide
|
|
||||||
world of Python's libraries (why yes, you now can write a Django
|
|
||||||
application in Lisp!)
|
|
||||||
- For Pythonistas, a great way to start exploring Lisp, from the
|
|
||||||
comfort of Python!
|
|
||||||
- For everyone: a pleasant language that has a lot of neat ideas!
|
|
||||||
|
|
||||||
|
This program calls the :func:`print` function, which, like all of Python's
|
||||||
|
:ref:`built-in functions <py:built-in-funcs>`, is available in Hy.
|
||||||
|
|
||||||
Basic intro to Lisp for Pythonistas
|
All of Python's :ref:`binary and unary operators <py:expressions>` are
|
||||||
===================================
|
available, too, although ``==`` is spelled ``=`` in deference to Lisp
|
||||||
|
tradition. Here's how we'd use the addition operator ``+``::
|
||||||
Okay, maybe you've never used Lisp before, but you've used Python!
|
|
||||||
|
|
||||||
A "hello world" program in Hy is actually super simple. Let's try it:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(print "hello world")
|
|
||||||
|
|
||||||
See? Easy! As you may have guessed, this is the same as the Python
|
|
||||||
version of::
|
|
||||||
|
|
||||||
print("hello world")
|
|
||||||
|
|
||||||
To add up some super simple math, we could do:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(+ 1 3)
|
(+ 1 3)
|
||||||
|
|
||||||
Which would return 4 and would be the equivalent of:
|
This code returns ``4``. It's equivalent to ``1 + 3`` in Python and many other
|
||||||
|
languages. Languages in the `Lisp
|
||||||
|
<https://en.wikipedia.org/wiki/Lisp_(programming_language)>`_ family, including
|
||||||
|
Hy, use a prefix syntax: ``+``, just like ``print`` or ``sqrt``, appears before
|
||||||
|
all of its arguments. The call is delimited by parentheses, but the opening
|
||||||
|
parenthesis appears before the operator being called instead of after it, so
|
||||||
|
instead of ``sqrt(2)``, we write ``(sqrt 2)``. Multiple arguments, such as the
|
||||||
|
two integers in ``(+ 1 3)``, are separated by whitespace. Many operators,
|
||||||
|
including ``+``, allow more than two arguments: ``(+ 1 2 3)`` is equivalent to
|
||||||
|
``1 + 2 + 3``.
|
||||||
|
|
||||||
.. code-block:: clj
|
Here's a more complex example::
|
||||||
|
|
||||||
1 + 3
|
(- (* (+ 1 3 88) 2) 8)
|
||||||
|
|
||||||
What you'll notice is that the first item in the list is the function
|
This code returns ``176``. Why? We can see the infix equivalent with the
|
||||||
being called and the rest of the arguments are the arguments being
|
command ``echo "(- (* (+ 1 3 88) 2) 8)" | hy2py``, which returns the Python
|
||||||
passed in. In fact, in Hy (as with most Lisps) we can pass in
|
code corresponding to the given Hy code, or by passing the ``--spy`` option to
|
||||||
multiple arguments to the plus operator:
|
Hy when starting the REPL, which shows the Python equivalent of each input line
|
||||||
|
before the result. The infix equivalent in this case is:
|
||||||
|
|
||||||
.. code-block:: clj
|
.. code-block:: python
|
||||||
|
|
||||||
(+ 1 3 55)
|
((1 + 3 + 88) * 2) - 8
|
||||||
|
|
||||||
Which would return 59.
|
To evaluate this infix expression, you'd of course evaluate the innermost
|
||||||
|
parenthesized expression first and work your way outwards. The same goes for
|
||||||
|
Lisp. Here's what we'd get by evaluating the above Hy code one step at a time::
|
||||||
|
|
||||||
Maybe you've heard of Lisp before but don't know much about it. Lisp
|
(- (* (+ 1 3 88) 2) 8)
|
||||||
isn't as hard as you might think, and Hy inherits from Python, so Hy
|
(- (* 92 2) 8)
|
||||||
is a great way to start learning Lisp. The main thing that's obvious
|
(- 184 8)
|
||||||
about Lisp is that there's a lot of parentheses. This might seem
|
176
|
||||||
confusing at first, but it isn't so hard. Let's look at some simple
|
|
||||||
math that's wrapped in a bunch of parentheses that we could enter into
|
|
||||||
the Hy interpreter:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
The basic unit of Lisp syntax, which is similar to a C or Python expression, is
|
||||||
|
the **form**. ``92``, ``*``, and ``(* 92 2)`` are all forms. A Lisp program
|
||||||
|
consists of a sequence of forms nested within forms. Forms are typically
|
||||||
|
separated from each other by whitespace, but some forms, such as string
|
||||||
|
literals (``"Hy, world!"``), can contain whitespace themselves. An
|
||||||
|
**expression** is a form enclosed in parentheses; its first child form, called
|
||||||
|
the **head**, determines what the expression does, and should generally be a
|
||||||
|
function, macro, or special operator. Functions are the most ordinary sort of
|
||||||
|
head, whereas macros (described in more detail below) are functions executed at
|
||||||
|
compile-time instead and return code to be executed at run-time. Special
|
||||||
|
operators are one of :ref:`a fixed set of names <special-forms>` that are
|
||||||
|
hard-coded into the compiler, and used to implement everything else.
|
||||||
|
|
||||||
(setv result (- (/ (+ 1 3 88) 2) 8))
|
Comments start with a ``;`` character and continue till the end of the line. A
|
||||||
|
comment is functionally equivalent to whitespace. ::
|
||||||
|
|
||||||
This would return 38.0 But why? Well, we could look at the equivalent
|
(print (** 2 64)) ; Max 64-bit unsigned integer value
|
||||||
expression in python::
|
|
||||||
|
|
||||||
result = ((1 + 3 + 88) / 2) - 8
|
Although ``#`` isn't a comment character in Hy, a Hy program can begin with a
|
||||||
|
`shebang line <https://en.wikipedia.org/wiki/Shebang_(Unix)>`_, which Hy itself
|
||||||
|
will ignore::
|
||||||
|
|
||||||
If you were to try to figure out how the above were to work in python,
|
#!/usr/bin/env hy
|
||||||
you'd of course figure out the results by solving each inner
|
(print "Make me executable, and run me!")
|
||||||
parenthesis. That's the same basic idea in Hy. Let's try this
|
|
||||||
exercise first in Python::
|
|
||||||
|
|
||||||
result = ((1 + 3 + 88) / 2) - 8
|
Literals
|
||||||
# simplified to...
|
========
|
||||||
result = (92 / 2) - 8
|
|
||||||
# simplified to...
|
|
||||||
result = 46.0 - 8
|
|
||||||
# simplified to...
|
|
||||||
result = 38.0
|
|
||||||
|
|
||||||
Now let's try the same thing in Hy:
|
Hy has :ref:`literal syntax <syntax>` for all of the same data types that
|
||||||
|
Python does. Here's an example of Hy code for each type and the Python
|
||||||
|
equivalent.
|
||||||
|
|
||||||
.. code-block:: clj
|
============== ================ =================
|
||||||
|
Hy Python Type
|
||||||
|
============== ================ =================
|
||||||
|
``1`` ``1`` :class:`int`
|
||||||
|
``1.2`` ``1.2`` :class:`float`
|
||||||
|
``4j`` ``4j`` :class:`complex`
|
||||||
|
``True`` ``True`` :class:`bool`
|
||||||
|
``None`` ``None`` :class:`NoneType`
|
||||||
|
``"hy"`` ``'hy'`` :class:`str`
|
||||||
|
``b"hy"`` ``b'hy'`` :class:`bytes`
|
||||||
|
``(, 1 2 3)`` ``(1, 2, 3)`` :class:`tuple`
|
||||||
|
``[1 2 3]`` ``[1, 2, 3]`` :class:`list`
|
||||||
|
``#{1 2 3}`` ``{1, 2, 3}`` :class:`set`
|
||||||
|
``{1 2 3 4}`` ``{1: 2, 3: 4}`` :class:`dict`
|
||||||
|
============== ================ =================
|
||||||
|
|
||||||
(setv result (- (/ (+ 1 3 88) 2) 8))
|
In addition, Hy has a Clojure-style literal syntax for
|
||||||
; simplified to...
|
:class:`fractions.Fraction`: ``1/3`` is equivalent to ``fractions.Fraction(1,
|
||||||
(setv result (- (/ 92 2) 8))
|
3)``.
|
||||||
; simplified to...
|
|
||||||
(setv result (- 46.0 8))
|
|
||||||
; simplified to...
|
|
||||||
(setv result 38.0)
|
|
||||||
|
|
||||||
As you probably guessed, this last expression with ``setv`` means to
|
The Hy REPL prints output in Python syntax by default::
|
||||||
assign the variable "result" to 38.0.
|
|
||||||
|
|
||||||
See? Not too hard!
|
|
||||||
|
|
||||||
This is the basic premise of Lisp. Lisp stands for "list
|
|
||||||
processing"; this means that the structure of the program is
|
|
||||||
actually lists of lists. (If you're familiar with Python lists,
|
|
||||||
imagine the entire same structure as above but with square brackets
|
|
||||||
instead, and you'll be able to see the structure above as both a
|
|
||||||
program and a data structure.) This is easier to understand with more
|
|
||||||
examples, so let's write a simple Python program, test it, and then
|
|
||||||
show the equivalent Hy program::
|
|
||||||
|
|
||||||
def simple_conversation():
|
|
||||||
print("Hello! I'd like to get to know you. Tell me about yourself!")
|
|
||||||
name = input("What is your name? ")
|
|
||||||
age = input("What is your age? ")
|
|
||||||
print("Hello " + name + "! I see you are " + age + " years old.")
|
|
||||||
|
|
||||||
simple_conversation()
|
|
||||||
|
|
||||||
If we ran this program, it might go like::
|
|
||||||
|
|
||||||
Hello! I'd like to get to know you. Tell me about yourself!
|
|
||||||
What is your name? Gary
|
|
||||||
What is your age? 38
|
|
||||||
Hello Gary! I see you are 38 years old.
|
|
||||||
|
|
||||||
Now let's look at the equivalent Hy program:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(defn simple-conversation []
|
|
||||||
(print "Hello! I'd like to get to know you. Tell me about yourself!")
|
|
||||||
(setv name (input "What is your name? "))
|
|
||||||
(setv age (input "What is your age? "))
|
|
||||||
(print (+ "Hello " name "! I see you are "
|
|
||||||
age " years old.")))
|
|
||||||
|
|
||||||
(simple-conversation)
|
|
||||||
|
|
||||||
If you look at the above program, as long as you remember that the
|
|
||||||
first element in each list of the program is the function (or
|
|
||||||
macro... we'll get to those later) being called and that the rest are
|
|
||||||
the arguments, it's pretty easy to figure out what this all means.
|
|
||||||
(As you probably also guessed, ``defn`` is the Hy method of defining
|
|
||||||
methods.)
|
|
||||||
|
|
||||||
Still, lots of people find this confusing at first because there's so
|
|
||||||
many parentheses, but there are plenty of things that can help make
|
|
||||||
this easier: keep indentation nice and use an editor with parenthesis
|
|
||||||
matching (this will help you figure out what each parenthesis pairs up
|
|
||||||
with) and things will start to feel comfortable.
|
|
||||||
|
|
||||||
There are some advantages to having a code structure that's actually a
|
|
||||||
very simple data structure as the core of Lisp is based on. For one
|
|
||||||
thing, it means that your programs are easy to parse and that the
|
|
||||||
entire actual structure of the program is very clearly exposed to you.
|
|
||||||
(There's an extra step in Hy where the structure you see is converted
|
|
||||||
to Python's own representations ... in "purer" Lisps such as Common
|
|
||||||
Lisp or Emacs Lisp, the data structure you see in the code and the
|
|
||||||
data structure that is executed is much more literally close.)
|
|
||||||
|
|
||||||
Another implication of this is macros: if a program's structure is a
|
|
||||||
simple data structure, that means you can write code that can write
|
|
||||||
code very easily, meaning that implementing entirely new language
|
|
||||||
features can be very fast. Previous to Hy, this wasn't very possible
|
|
||||||
for Python programmers ... now you too can make use of macros'
|
|
||||||
incredible power (just be careful to not aim them footward)!
|
|
||||||
|
|
||||||
|
|
||||||
Hy is a Lisp-flavored Python
|
|
||||||
============================
|
|
||||||
|
|
||||||
Hy converts to Python's own abstract syntax tree, so you'll soon start
|
|
||||||
to find that all the familiar power of python is at your fingertips.
|
|
||||||
|
|
||||||
You have full access to Python's data types and standard library in
|
|
||||||
Hy. Let's experiment with this in the hy interpreter::
|
|
||||||
|
|
||||||
=> [1 2 3]
|
=> [1 2 3]
|
||||||
[1, 2, 3]
|
[1, 2, 3]
|
||||||
=> {"dog" "bark"
|
|
||||||
... "cat" "meow"}
|
|
||||||
{'dog': 'bark', 'cat': 'meow'}
|
|
||||||
=> (, 1 2 3)
|
|
||||||
(1, 2, 3)
|
|
||||||
=> #{3 1 2}
|
|
||||||
{1, 2, 3}
|
|
||||||
=> 1/2
|
|
||||||
Fraction(1, 2)
|
|
||||||
|
|
||||||
Notice the last two lines: Hy has a fraction literal like Clojure.
|
But if you start Hy like this (a shell alias might be helpful)::
|
||||||
|
|
||||||
If you start Hy like this (a shell alias might be helpful)::
|
|
||||||
|
|
||||||
$ hy --repl-output-fn=hy.contrib.hy-repr.hy-repr
|
$ hy --repl-output-fn=hy.contrib.hy-repr.hy-repr
|
||||||
|
|
||||||
the interactive mode will use :ref:`hy-repr-fn` instead of Python's
|
the interactive mode will use :ref:`hy-repr-fn` instead of Python's native
|
||||||
native ``repr`` function to print out values, so you'll see values in
|
``repr`` function to print out values, so you'll see values in Hy syntax::
|
||||||
Hy syntax rather than Python syntax::
|
|
||||||
|
|
||||||
=> [1 2 3]
|
=> [1 2 3]
|
||||||
[1 2 3]
|
[1 2 3]
|
||||||
=> {"dog" "bark"
|
|
||||||
... "cat" "meow"}
|
|
||||||
{"dog" "bark" "cat" "meow"}
|
|
||||||
|
|
||||||
If you are familiar with other Lisps, you may be interested that Hy
|
|
||||||
supports the Common Lisp method of quoting:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
Basic operations
|
||||||
|
================
|
||||||
|
|
||||||
=> '(1 2 3)
|
Set variables with :ref:`setv`::
|
||||||
(1 2 3)
|
|
||||||
|
|
||||||
You also have access to all the built-in types' nice methods::
|
(setv zone-plane 8)
|
||||||
|
|
||||||
=> (.strip " fooooo ")
|
Access the elements of a list, dictionary, or other data structure with
|
||||||
"fooooo"
|
:ref:`get`::
|
||||||
|
|
||||||
What's this? Yes indeed, this is precisely the same as::
|
(setv fruit ["apple" "banana" "cantaloupe"])
|
||||||
|
(print (get fruit 0)) ; => apple
|
||||||
|
(setv (get fruit 1) "durian")
|
||||||
|
(print (get fruit 1)) ; => durian
|
||||||
|
|
||||||
" fooooo ".strip()
|
Access a range of elements in an ordered structure with :ref:`cut`::
|
||||||
|
|
||||||
That's right---Lisp with dot notation! If we have this string
|
(print (cut "abcdef" 1 4)) ; => bcd
|
||||||
assigned as a variable, we can also do the following:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
Conditional logic can be built with :ref:`if`::
|
||||||
|
|
||||||
(setv this-string " fooooo ")
|
(if (= 1 1)
|
||||||
(this-string.strip)
|
(print "Math works. The universe is safe.")
|
||||||
|
(print "Math has failed. The universe is doomed."))
|
||||||
|
|
||||||
What about conditionals?:
|
As in this example, ``if`` is called like ``(if CONDITION THEN ELSE)``. It
|
||||||
|
executes and returns the form ``THEN`` if ``CONDITION`` is true (according to
|
||||||
|
:class:`bool`) and ``ELSE`` otherwise. If ``ELSE`` is omitted, ``None`` is used
|
||||||
|
in its place.
|
||||||
|
|
||||||
.. code-block:: clj
|
What if you want to use more than form in place of the ``THEN`` or ``ELSE``
|
||||||
|
clauses, or in place of ``CONDITION``, for that matter? Use the special
|
||||||
|
operator :ref:`do` (known more traditionally in Lisp as ``progn``), which
|
||||||
|
combines several forms into one, returning the last::
|
||||||
|
|
||||||
(if (try-some-thing)
|
(if (do (print "Let's check.") (= 1 1))
|
||||||
(print "this is if true")
|
(do
|
||||||
(print "this is if false"))
|
(print "Math works.")
|
||||||
|
(print "The universe is safe."))
|
||||||
|
(do
|
||||||
|
(print "Math has failed.")
|
||||||
|
(print "The universe is doomed.")))
|
||||||
|
|
||||||
As you can tell above, the first argument to ``if`` is a truth test, the
|
For branching on more than one case, try :ref:`cond`::
|
||||||
second argument is the body if true, and the third argument (optional!)
|
|
||||||
is if false (ie. ``else``).
|
|
||||||
|
|
||||||
If you need to do more complex conditionals, you'll find that you
|
|
||||||
don't have ``elif`` available in Hy. Instead, you should use something
|
|
||||||
called ``cond``. In Python, you might do something like::
|
|
||||||
|
|
||||||
somevar = 33
|
|
||||||
if somevar > 50:
|
|
||||||
print("That variable is too big!")
|
|
||||||
elif somevar < 10:
|
|
||||||
print("That variable is too small!")
|
|
||||||
else:
|
|
||||||
print("That variable is jussssst right!")
|
|
||||||
|
|
||||||
In Hy, you would do:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(setv somevar 33)
|
(setv somevar 33)
|
||||||
(cond
|
(cond
|
||||||
@ -278,306 +180,140 @@ In Hy, you would do:
|
|||||||
[True
|
[True
|
||||||
(print "That variable is jussssst right!")])
|
(print "That variable is jussssst right!")])
|
||||||
|
|
||||||
What you'll notice is that ``cond`` switches off between a statement
|
The macro ``(when CONDITION THEN-1 THEN-2 …)`` is shorthand for ``(if CONDITION
|
||||||
that is executed and checked conditionally for true or falseness, and
|
(do THEN-1 THEN-2 …))``. ``unless`` works the same as ``when``, but inverts the
|
||||||
then a bit of code to execute if it turns out to be true. You'll also
|
condition with ``not``.
|
||||||
notice that the ``else`` is implemented at the end simply by checking
|
|
||||||
for ``True`` -- that's because ``True`` will always be true, so if we get
|
|
||||||
this far, we'll always run that one!
|
|
||||||
|
|
||||||
You might notice above that if you have code like:
|
Hy's basic loops are :ref:`while` and :ref:`for`::
|
||||||
|
|
||||||
.. code-block:: clj
|
(setv x 3)
|
||||||
|
(while (> x 0)
|
||||||
|
(print x)
|
||||||
|
(setv x (- x 1))) ; => 3 2 1
|
||||||
|
|
||||||
(if some-condition
|
(for [x [1 2 3]]
|
||||||
(body-if-true)
|
(print x)) ; => 1 2 3
|
||||||
(body-if-false))
|
|
||||||
|
|
||||||
But wait! What if you want to execute more than one statement in the
|
A more functional way to iterate is provided by the comprehension forms such as
|
||||||
body of one of these?
|
:ref:`lfor`. Whereas ``for`` always returns ``None``, ``lfor`` returns a list
|
||||||
|
with one element per iteration. ::
|
||||||
|
|
||||||
You can do the following:
|
(print (lfor x [1 2 3] (* x 2))) ; => [2, 4, 6]
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(if (try-some-thing)
|
Functions, classes, and modules
|
||||||
(do
|
===============================
|
||||||
(print "this is if true")
|
|
||||||
(print "and why not, let's keep talking about how true it is!"))
|
|
||||||
(print "this one's still simply just false"))
|
|
||||||
|
|
||||||
You can see that we used ``do`` to wrap multiple statements. If you're
|
Define named functions with :ref:`defn`::
|
||||||
familiar with other Lisps, this is the equivalent of ``progn``
|
|
||||||
elsewhere.
|
|
||||||
|
|
||||||
Comments start with semicolons:
|
(defn fib [n]
|
||||||
|
(if (< n 2)
|
||||||
|
n
|
||||||
|
(+ (fib (- n 1)) (fib (- n 2)))))
|
||||||
|
(print (fib 8)) ; => 21
|
||||||
|
|
||||||
.. code-block:: clj
|
Define anonymous functions with :ref:`fn`::
|
||||||
|
|
||||||
(print "this will run")
|
(print (list (filter (fn [x] (% x 2)) (range 10))))
|
||||||
; (print "but this will not")
|
; => [1, 3, 5, 7, 9]
|
||||||
(+ 1 2 3) ; we'll execute the addition, but not this comment!
|
|
||||||
|
|
||||||
Hashbang (``#!``) syntax is supported:
|
Special symbols in the parameter list of ``defn`` or ``fn`` allow you to
|
||||||
|
indicate optional arguments, provide default values, and collect unlisted
|
||||||
|
arguments::
|
||||||
|
|
||||||
.. code-block:: clj
|
(defn test [a b &optional c [d "x"] &rest e]
|
||||||
|
[a b c d e])
|
||||||
|
(print (test 1 2)) ; => [1, 2, None, 'x', ()]
|
||||||
|
(print (test 1 2 3 4 5 6 7)) ; => [1, 2, 3, 4, (5, 6, 7)]
|
||||||
|
|
||||||
#! /usr/bin/env hy
|
Set a function parameter by name with a ``:keyword``::
|
||||||
(print "Make me executable, and run me!")
|
|
||||||
|
|
||||||
Looping is not hard but has a kind of special structure. In Python,
|
(test 1 2 :d "y") ; => [1, 2, None, 'y', ()]
|
||||||
we might do::
|
|
||||||
|
|
||||||
for i in range(10):
|
Define classes with :ref:`defclass`::
|
||||||
print("'i' is now at " + str(i))
|
|
||||||
|
|
||||||
The equivalent in Hy would be:
|
(defclass FooBar []
|
||||||
|
(defn __init__ [self x]
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(for [i (range 10)]
|
|
||||||
(print (+ "'i' is now at " (str i))))
|
|
||||||
|
|
||||||
Python's collections indexes and slices are implemented
|
|
||||||
by the ``get`` and ``cut`` built-in:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(setv array [0 1 2])
|
|
||||||
(get array 1)
|
|
||||||
(cut array -3 -1)
|
|
||||||
|
|
||||||
which is equivalent to::
|
|
||||||
|
|
||||||
array[1]
|
|
||||||
array[-3:-1]
|
|
||||||
|
|
||||||
You can also import and make use of various Python libraries. For
|
|
||||||
example:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(import os)
|
|
||||||
|
|
||||||
(if (os.path.isdir "/tmp/somedir")
|
|
||||||
(os.mkdir "/tmp/somedir/anotherdir")
|
|
||||||
(print "Hey, that path isn't there!"))
|
|
||||||
|
|
||||||
Python's context managers (``with`` statements) are used like this:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(with [f (open "/tmp/data.in")]
|
|
||||||
(print (.read f)))
|
|
||||||
|
|
||||||
which is equivalent to::
|
|
||||||
|
|
||||||
with open("/tmp/data.in") as f:
|
|
||||||
print(f.read())
|
|
||||||
|
|
||||||
And yes, we do have List comprehensions! In Python you might do::
|
|
||||||
|
|
||||||
odds_squared = [
|
|
||||||
pow(num, 2)
|
|
||||||
for num in range(100)
|
|
||||||
if num % 2 == 1]
|
|
||||||
|
|
||||||
In Hy, you could do these like:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(setv odds-squared
|
|
||||||
(lfor
|
|
||||||
num (range 100)
|
|
||||||
:if (= (% num 2) 1)
|
|
||||||
(pow num 2)))
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
; And, an example stolen shamelessly from a Clojure page:
|
|
||||||
; Let's list all the blocks of a Chessboard:
|
|
||||||
|
|
||||||
(lfor
|
|
||||||
x (range 8)
|
|
||||||
y "ABCDEFGH"
|
|
||||||
(, x y))
|
|
||||||
|
|
||||||
; [(0, 'A'), (0, 'B'), (0, 'C'), (0, 'D'), (0, 'E'), (0, 'F'), (0, 'G'), (0, 'H'),
|
|
||||||
; (1, 'A'), (1, 'B'), (1, 'C'), (1, 'D'), (1, 'E'), (1, 'F'), (1, 'G'), (1, 'H'),
|
|
||||||
; (2, 'A'), (2, 'B'), (2, 'C'), (2, 'D'), (2, 'E'), (2, 'F'), (2, 'G'), (2, 'H'),
|
|
||||||
; (3, 'A'), (3, 'B'), (3, 'C'), (3, 'D'), (3, 'E'), (3, 'F'), (3, 'G'), (3, 'H'),
|
|
||||||
; (4, 'A'), (4, 'B'), (4, 'C'), (4, 'D'), (4, 'E'), (4, 'F'), (4, 'G'), (4, 'H'),
|
|
||||||
; (5, 'A'), (5, 'B'), (5, 'C'), (5, 'D'), (5, 'E'), (5, 'F'), (5, 'G'), (5, 'H'),
|
|
||||||
; (6, 'A'), (6, 'B'), (6, 'C'), (6, 'D'), (6, 'E'), (6, 'F'), (6, 'G'), (6, 'H'),
|
|
||||||
; (7, 'A'), (7, 'B'), (7, 'C'), (7, 'D'), (7, 'E'), (7, 'F'), (7, 'G'), (7, 'H')]
|
|
||||||
|
|
||||||
|
|
||||||
Python has support for various fancy argument and keyword arguments.
|
|
||||||
In Python we might see::
|
|
||||||
|
|
||||||
>>> def optional_arg(pos1, pos2, keyword1=None, keyword2=42):
|
|
||||||
... return [pos1, pos2, keyword1, keyword2]
|
|
||||||
...
|
|
||||||
>>> optional_arg(1, 2)
|
|
||||||
[1, 2, None, 42]
|
|
||||||
>>> optional_arg(1, 2, 3, 4)
|
|
||||||
[1, 2, 3, 4]
|
|
||||||
>>> optional_arg(keyword1=1, pos2=2, pos1=3, keyword2=4)
|
|
||||||
[3, 2, 1, 4]
|
|
||||||
|
|
||||||
The same thing in Hy::
|
|
||||||
|
|
||||||
=> (defn optional-arg [pos1 pos2 &optional keyword1 [keyword2 42]]
|
|
||||||
... [pos1 pos2 keyword1 keyword2])
|
|
||||||
=> (optional-arg 1 2)
|
|
||||||
[1 2 None 42]
|
|
||||||
=> (optional-arg 1 2 3 4)
|
|
||||||
[1 2 3 4]
|
|
||||||
|
|
||||||
You can call keyword arguments like this::
|
|
||||||
|
|
||||||
=> (optional-arg :keyword1 1
|
|
||||||
... :pos2 2
|
|
||||||
... :pos1 3
|
|
||||||
... :keyword2 4)
|
|
||||||
[3, 2, 1, 4]
|
|
||||||
|
|
||||||
You can unpack arguments with the syntax ``#* args`` and ``#** kwargs``,
|
|
||||||
similar to `*args` and `**kwargs` in Python::
|
|
||||||
|
|
||||||
=> (setv args [1 2])
|
|
||||||
=> (setv kwargs {"keyword2" 3
|
|
||||||
... "keyword1" 4})
|
|
||||||
=> (optional-arg #* args #** kwargs)
|
|
||||||
[1, 2, 4, 3]
|
|
||||||
|
|
||||||
Hy also supports ``*args`` and ``**kwargs`` in parameter lists. In Python::
|
|
||||||
|
|
||||||
def some_func(foo, bar, *args, **kwargs):
|
|
||||||
import pprint
|
|
||||||
pprint.pprint((foo, bar, args, kwargs))
|
|
||||||
|
|
||||||
The Hy equivalent:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(defn some-func [foo bar &rest args &kwargs kwargs]
|
|
||||||
(import pprint)
|
|
||||||
(pprint.pprint (, foo bar args kwargs)))
|
|
||||||
|
|
||||||
Finally, of course we need classes! In Python, we might have a class
|
|
||||||
like::
|
|
||||||
|
|
||||||
class FooBar(object):
|
|
||||||
"""
|
|
||||||
Yet Another Example Class
|
|
||||||
"""
|
|
||||||
def __init__(self, x):
|
|
||||||
self.x = x
|
|
||||||
|
|
||||||
def get_x(self):
|
|
||||||
"""
|
|
||||||
Return our copy of x
|
|
||||||
"""
|
|
||||||
return self.x
|
|
||||||
|
|
||||||
And we might use it like::
|
|
||||||
|
|
||||||
bar = FooBar(1)
|
|
||||||
print(bar.get_x())
|
|
||||||
|
|
||||||
|
|
||||||
In Hy:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(defclass FooBar [object]
|
|
||||||
"Yet Another Example Class"
|
|
||||||
|
|
||||||
(defn --init-- [self x]
|
|
||||||
(setv self.x x))
|
(setv self.x x))
|
||||||
|
|
||||||
(defn get-x [self]
|
(defn get-x [self]
|
||||||
"Return our copy of x"
|
|
||||||
self.x))
|
self.x))
|
||||||
|
|
||||||
And we can use it like:
|
Here we create a new instance ``fb`` of ``FooBar`` and access its attributes by
|
||||||
|
various means::
|
||||||
|
|
||||||
.. code-block:: clj
|
(setv fb (FooBar 15))
|
||||||
|
(print fb.x) ; => 15
|
||||||
|
(print (. fb x)) ; => 15
|
||||||
|
(print (.get-x fb)) ; => 15
|
||||||
|
(print (fb.get-x)) ; => 15
|
||||||
|
|
||||||
(setv bar (FooBar 1))
|
Note that syntax like ``fb.x`` and ``fb.get-x`` only works when the object
|
||||||
(print (bar.get-x))
|
being invoked (``fb``, in this case) is a simple variable name. To get an
|
||||||
|
attribute or call a method of an arbitrary form ``FORM``, you must use the
|
||||||
|
syntax ``(. FORM x)`` or ``(.get-x FORM)``.
|
||||||
|
|
||||||
Or using the leading dot syntax!
|
Access an external module, whether written in Python or Hy, with
|
||||||
|
:ref:`import`::
|
||||||
|
|
||||||
.. code-block:: clj
|
(import math)
|
||||||
|
(print (math.sqrt 2)) ; => 1.4142135623730951
|
||||||
|
|
||||||
(print (.get-x (FooBar 1)))
|
Python can import a Hy module like any other module so long as Hy itself has
|
||||||
|
been imported first, which, of course, must have already happened if you're
|
||||||
|
running a Hy program.
|
||||||
You can also do class-level attributes. In Python::
|
|
||||||
|
|
||||||
class Customer(models.Model):
|
|
||||||
name = models.CharField(max_length=255)
|
|
||||||
address = models.TextField()
|
|
||||||
notes = models.TextField()
|
|
||||||
|
|
||||||
In Hy:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(defclass Customer [models.Model]
|
|
||||||
(setv name (models.CharField :max-length 255))
|
|
||||||
(setv address (models.TextField))
|
|
||||||
(setv notes (models.TextField)))
|
|
||||||
|
|
||||||
Macros
|
Macros
|
||||||
======
|
======
|
||||||
|
|
||||||
One really powerful feature of Hy are macros. They are small functions that are
|
Macros are the basic metaprogramming tool of Lisp. A macro is a function that
|
||||||
used to generate code (or data). When a program written in Hy is started, the
|
is called at compile time (i.e., when a Hy program is being translated to
|
||||||
macros are executed and their output is placed in the program source. After this,
|
Python :mod:`ast` objects) and returns code, which becomes part of the final
|
||||||
the program starts executing normally. Very simple example:
|
program. Here's a simple example::
|
||||||
|
|
||||||
.. code-block:: clj
|
(print "Executing")
|
||||||
|
(defmacro m []
|
||||||
|
(print "Now for a slow computation")
|
||||||
|
(setv x (% (** 10 10 7) 3))
|
||||||
|
(print "Done computing")
|
||||||
|
x)
|
||||||
|
(print "Value:" (m))
|
||||||
|
(print "Done executing")
|
||||||
|
|
||||||
=> (defmacro hello [person]
|
If you run this program twice in a row, you'll see this::
|
||||||
... `(print "Hello there," ~person))
|
|
||||||
=> (hello "Tuukka")
|
|
||||||
Hello there, Tuukka
|
|
||||||
|
|
||||||
The thing to notice here is that hello macro doesn't output anything on
|
$ hy example.hy
|
||||||
screen. Instead it creates piece of code that is then executed and prints on
|
Now for a slow computation
|
||||||
screen. This macro writes a piece of program that looks like this (provided that
|
Done computing
|
||||||
we used "Tuukka" as parameter):
|
Executing
|
||||||
|
Value: 1
|
||||||
|
Done executing
|
||||||
|
$ hy example.hy
|
||||||
|
Executing
|
||||||
|
Value: 1
|
||||||
|
Done executing
|
||||||
|
|
||||||
.. code-block:: clj
|
The slow computation is performed while compiling the program on its first
|
||||||
|
invocation. Only after the whole program is compiled does normal execution
|
||||||
|
begin from the top, printing "Executing". When the program is called a second
|
||||||
|
time, it is run from the previously compiled bytecode, which is equivalent to
|
||||||
|
simply::
|
||||||
|
|
||||||
(print "Hello there," "Tuukka")
|
(print "Executing")
|
||||||
|
(print "Value:" 1)
|
||||||
|
(print "Done executing")
|
||||||
|
|
||||||
We can also manipulate code with macros:
|
Our macro ``m`` has an especially simple return value, an integer, which at
|
||||||
|
compile-time is converted to an integer literal. In general, macros can return
|
||||||
.. code-block:: clj
|
arbitrary Hy forms to be executed as code. There are several special operators
|
||||||
|
and macros that make it easy to construct forms programmatically, such as
|
||||||
=> (defmacro rev [code]
|
:ref:`quote` (``'``), :ref:`quasiquote` (`````), :ref:`unquote` (``~``), and
|
||||||
... (setv op (last code) params (list (butlast code)))
|
:ref:`defmacro!`. The previous chapter has :ref:`a simple example <do-while>`
|
||||||
... `(~op ~@params))
|
of using ````` and ``~`` to define a new control construct ``do-while``.
|
||||||
=> (rev (1 2 3 +))
|
|
||||||
6
|
|
||||||
|
|
||||||
The code that was generated with this macro just switched around some of the
|
|
||||||
elements, so by the time program started executing, it actually reads:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(+ 1 2 3)
|
|
||||||
|
|
||||||
Sometimes it's nice to be able to call a one-parameter macro without
|
Sometimes it's nice to be able to call a one-parameter macro without
|
||||||
parentheses. Tag macros allow this. The name of a tag macro is typically
|
parentheses. Tag macros allow this. The name of a tag macro is often just one
|
||||||
one character long, but since Hy operates well with Unicode, we aren't running
|
character long, but since Hy allows most Unicode characters in the name of a
|
||||||
out of characters that soon:
|
macro (or ordinary variable), you won't out of characters soon. ::
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
=> (deftag ↻ [code]
|
=> (deftag ↻ [code]
|
||||||
... (setv op (last code) params (list (butlast code)))
|
... (setv op (last code) params (list (butlast code)))
|
||||||
@ -585,106 +321,23 @@ out of characters that soon:
|
|||||||
=> #↻(1 2 3 +)
|
=> #↻(1 2 3 +)
|
||||||
6
|
6
|
||||||
|
|
||||||
Macros are useful when one wishes to extend Hy or write their own
|
What if you want to use a macro that's defined in a different module?
|
||||||
language on top of that. Many features of Hy are macros, like ``when``,
|
``import`` won't help, because it merely translates to a Python ``import``
|
||||||
``cond`` and ``->``.
|
statement that's executed at run-time, and macros are expanded at compile-time,
|
||||||
|
that is, during the translation from Hy to Python. Instead, use :ref:`require`,
|
||||||
What if you want to use a macro that's defined in a different
|
which imports the module and makes macros available at compile-time.
|
||||||
module? The special form ``import`` won't help, because it merely
|
``require`` uses the same syntax as ``import``. ::
|
||||||
translates to a Python ``import`` statement that's executed at
|
|
||||||
run-time, and macros are expanded at compile-time, that is,
|
|
||||||
during the translate from Hy to Python. Instead, use ``require``,
|
|
||||||
which imports the module and makes macros available at
|
|
||||||
compile-time. ``require`` uses the same syntax as ``import``.
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
=> (require tutorial.macros)
|
=> (require tutorial.macros)
|
||||||
=> (tutorial.macros.rev (1 2 3 +))
|
=> (tutorial.macros.rev (1 2 3 +))
|
||||||
6
|
6
|
||||||
|
|
||||||
Hy <-> Python interop
|
Next steps
|
||||||
=====================
|
==========
|
||||||
|
|
||||||
Using Hy from Python
|
You now know enough to be dangerous with Hy. You may now smile villainously and
|
||||||
--------------------
|
sneak off to your Hydeaway to do unspeakable things.
|
||||||
|
|
||||||
You can use Hy modules in Python!
|
Refer to Python's documention for the details of Python semantics, and the rest
|
||||||
|
of this manual for Hy-specific features. Like Hy itself, the manual is
|
||||||
If you save the following in ``greetings.hy``:
|
incomplete, but :ref:`contributions <hacking>` are always welcome.
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(defn greet [name] (print "hello from hy," name))
|
|
||||||
|
|
||||||
Then you can use it directly from Python, by importing Hy before importing
|
|
||||||
the module. In Python::
|
|
||||||
|
|
||||||
import hy
|
|
||||||
import greetings
|
|
||||||
|
|
||||||
greetings.greet("Foo")
|
|
||||||
|
|
||||||
Using Python from Hy
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
You can also use any Python module in Hy!
|
|
||||||
|
|
||||||
If you save the following in ``greetings.py`` in Python::
|
|
||||||
|
|
||||||
def greet(name):
|
|
||||||
print("hello, %s" % (name))
|
|
||||||
|
|
||||||
You can use it in Hy (see :ref:`import`):
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(import greetings)
|
|
||||||
(.greet greetings "foo")
|
|
||||||
|
|
||||||
More information on :doc:`../language/interop`.
|
|
||||||
|
|
||||||
|
|
||||||
Protips!
|
|
||||||
========
|
|
||||||
|
|
||||||
Hy also features something known as the "threading macro", a really neat
|
|
||||||
feature of Clojure's. The "threading macro" (written as ``->``) is used
|
|
||||||
to avoid deep nesting of expressions.
|
|
||||||
|
|
||||||
The threading macro inserts each expression into the next expression's first
|
|
||||||
argument place.
|
|
||||||
|
|
||||||
Let's take the classic:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(require [hy.contrib.loop [loop]])
|
|
||||||
|
|
||||||
(loop (print (eval (read))))
|
|
||||||
|
|
||||||
Rather than write it like that, we can write it as follows:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(require [hy.contrib.loop [loop]])
|
|
||||||
|
|
||||||
(-> (read) (eval) (print) (loop))
|
|
||||||
|
|
||||||
Now, using `python-sh <http://amoffat.github.com/sh/>`_, we can show
|
|
||||||
how the threading macro (because of python-sh's setup) can be used like
|
|
||||||
a pipe:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
=> (import [sh [cat grep wc]])
|
|
||||||
=> (-> (cat "/usr/share/dict/words") (grep "-E" "^hy") (wc "-l"))
|
|
||||||
210
|
|
||||||
|
|
||||||
Which, of course, expands out to:
|
|
||||||
|
|
||||||
.. code-block:: clj
|
|
||||||
|
|
||||||
(wc (grep (cat "/usr/share/dict/words") "-E" "^hy") "-l")
|
|
||||||
|
|
||||||
Much more readable, no? Use the threading macro!
|
|
||||||
|
142
docs/whyhy.rst
Normal file
142
docs/whyhy.rst
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
=======
|
||||||
|
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 <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.
|
Loading…
Reference in New Issue
Block a user