
Hy – A dialect of Lisp that’s embedded in Python - colinprince
http://docs.hylang.org/en/latest/
======
agentultra
Hy is a self-compiling macro. Macros are just programmable mini-compilers
embedded in the host language. This lets you do neat things like take a
declarative description of a process and compile it into an executable
function:

[https://github.com/paultag/snitch/blob/master/example.hy](https://github.com/paultag/snitch/blob/master/example.hy)

I gave a talk at Pycon last year that discussed, among other things, the
implementation of a constraint solver for games. You can implement an answer-
set language on top of it in order to make it easier to use for yourself or
collaborators in pure-Python... but writing AST-transforming code using
Python's `ast` module is a huge pain. Just sprinkle some parens around and you
can treat the Python AST as if it were just another plain-old-datastructure.
It becomes magnitudes easier to write your answer-set solver's language
interface.

And because Hy is freely importable from Python code you can simply use it to
write that front-end compiler for your constraint solver. The rest of your
code can be in Python if that works better for you.

But it doesn't stop there... you also get Hy's core library. Which provides
some nice higher order functions and macros which compile down to really nice,
idiomatic Python code. The kind of code you'd want to write to express that
idea.

It's a nice tool to have.

(as a contributor I'm slightly biased).

------
rcarmo
FYI, this is what's been eating into my Clojure output. The Hy REPL starts up
instantly and the interop is so good I find myself writing small projects in
Hy instead.

Here's a wiki implemented with it (demonstrates using Bottle, doing URL
routing and few other things):
[http://github.com/rcarmo/sushy](http://github.com/rcarmo/sushy) ...and here's
a little playground with various snippets of code:
[https://github.com/rcarmo/hy-there](https://github.com/rcarmo/hy-there)

...and a feed fetcher: [https://github.com/rcarmo/mqtt-feed-
fetcher/blob/master/main...](https://github.com/rcarmo/mqtt-feed-
fetcher/blob/master/main.hy)

...and a prototype for a nicer REPL:
[https://github.com/rcarmo/hyrule](https://github.com/rcarmo/hyrule)

Edited to add: The killer feature here is cross-interop: I can re-use my code
in either direction (and have run Hy in PyPy, IronPython, etc. -- sadly, not
in Jython yet)

~~~
zem
i started migrating a project from clojure to common lisp so that i could move
the ui from swing to qt. i should really give hy + pyqt a try as well.

~~~
madmanwoo
pyqt works pretty well with it. I just gave it a try with QGIS (a open source
GIS application) and it uses PyQt

[http://nathanw.net/2014/12/04/using-hy-a-dialect-of-lisp-
for...](http://nathanw.net/2014/12/04/using-hy-a-dialect-of-lisp-for-python-
with-qgis/)

------
TazeTSchnitzel
Here's a minimal dialect of Lisp embedded in PHP, in 272 lines:

[https://github.com/igorw/yolo/blob/master/src/yolisp.php](https://github.com/igorw/yolo/blob/master/src/yolisp.php)

It could be shorter, but as written, it can use almost all PHP's operators (as
dynamically generated functions), interact with PHP classes and functions, and
produce closures that PHP code can call.

Actual code sample (albeit a poor one):

[https://github.com/igorw/yolo/blob/master/src/yolo.php#L19-3...](https://github.com/igorw/yolo/blob/master/src/yolo.php#L19-30)

Implementation of map could be done like this (tested and it works):

    
    
      y('let',
          y(y('map', y('lambda',
              y('func', 'data'),
              y('if', 
                  y('==', 'nil', 'data'),
                  'nil',
                  y('cons',
                      y('func', y('car', 'data')),
                      y('map', 'func', y('cdr', 'data'))
                  )
              )
          )),
          /* ... */
      )

~~~
sethrin
That's fascinating. What is performance like?

~~~
TazeTSchnitzel
I've done absolutely no profiling on it, but I suspect it isn't exactly
fantastic, particularly since any function call involves a linked-list to PHP
array conversion, then array to internal PHP parameter list conversion.

------
pixelmonkey
Related: an article comparing Python and Clojure, called "Clojonic: Pythonic
Clojure".

[http://www.pixelmonkey.org/2014/11/02/clojonic](http://www.pixelmonkey.org/2014/11/02/clojonic)

------
yzzxy
What are the particular advantages of a Lisp on the python runtime?

Interop with existing programs could be good, as well as a potential trojan
horse to convert a dev shop to Lispier languages if you care about that kind
of thing.

Otherwise I'm not sure - what python libraries stand out as particularly
useful in this situation? I haven't really interacted with the world of python
that much so I'd love to hear.

~~~
jarcane
Python has one of the biggest standard libraries there is, there is a _lot_ of
useful stuff in there already provided, plus there is a humongous amount of
third-party packages available through pip. It also generally has a pretty
good C FFI, so there's a lot more support of C-libs in Python.

It's not so much about any specific libraries as it is about the volume of
support one gets 'for free,' and as well I think Python syntax and semantics
alike really get along better with Lisp than some of the other dialects that
rely on FFIs (like Clojure to Java/JavaScript, or the number of C FFIs
available for Schemes).

Lisps in general don't tend to include much of a standard library, opting for
a 'batteries not included' model, with Racket being perhaps the most prominent
(and quite deliberate) exception. So the idea of getting the whole Python
ecosystem 'free' is potentially very appealing, especially to someone with a
Python background (and there are a lot of those someones around, especially
with it increasingly being a more popular teaching language than anything else
except JavaScript).

~~~
yzzxy
Racket (and precursor Scheme for academic reasons) is the Lisp I've interacted
the most with besides Clojure, so I never really considered the perspective of
a stdlib being so valuable. Clojure->Java and ClojureScript->Js have always
seemed super awkward to me, and having a similar naming style could be a great
feature.

Still, I'm reminded of Greenspun's tenth rule when reading about this project.
Hopefully it will become a popular and well-supported option in its own right.

~~~
jarcane
I find seeing a multipoint dotted Java name in front of an s-expression deeply
unsettling somehow, and I never learned Java anyway, so Clojure was of limited
utility to me since so much stuff seemed handed off to Java built-ins.

And I think there is an argument to be made against projects like this,
because they're so dependent, but I guess in the case of Hy, that dependence
is literally the point. I never felt like it was the case with Clojure,
because the host language was so very different from the experimental FP
playground Clojurists seem to want.

------
aidos
Can a lisper explain how things like the list comprehension might be
implemented? Is it a special control structure that hy knows about or is it
something you could implement yourself if it wasn't part of hy?

    
    
        (list-comp
          (, x y)
          (x (range 8)
           y "ABCDEFGH"))
    

The fact that data is bound to the variables x and y within the list
comprehension - is that a magical thing?

I'm probably not explaining it very well but I thought (as a non-lisper) that
you can do things like implement new types of control flow directly in lisp.
Could you do that here, or has list-comp been coded directly into the
language?

Edit: just to add - I think this is a cool project. Really enjoying reading
through the docs.

~~~
austinz
Python natively supports this sort of list comprehension
([https://docs.python.org/2/tutorial/datastructures.html#list-...](https://docs.python.org/2/tutorial/datastructures.html#list-
comprehensions)), and the Lisp source is being translated into a Python AST,
so that snippet is probably being directly mapped into whatever AST form is
necessary to invoke the list comprehension.

~~~
Foxboron
Yes thats correct.
[https://github.com/hylang/hy/blob/dcf29d3d2a147fe66a19971214...](https://github.com/hylang/hy/blob/dcf29d3d2a147fe66a199712149f7b58fd7fef04/hy/compiler.py#L1370)
Source for those interested. Note that Hy replaces all - in names for _, thus
we can have prettier names :)

~~~
rcarmo
Oh, and _earmuffs_ become EARMUFFS, which is also nice.

------
meric
It's beautiful. There's a strange feeling in my heart.

\- Someone who built a simplistic lisp->lua compiler a couple of years ago at
[https://github.com/meric/l2l](https://github.com/meric/l2l)

~~~
badsock
It's a kind of rite of passage, I think, to implement a Lisp. I wrote half
(the easy half) of ISLISP that ran on the Python VM back in 2005 :)

------
Crito
The combination of Hy and python-sh looks really interesting. I am tempted to
try using this as a shell, similar to scsh.

~~~
jimmcslim
By python-sh do you mean
[http://amoffat.github.io/sh/](http://amoffat.github.io/sh/) ?

~~~
Crito
Yes I do.

So I tried this out briefly and it works, but without additional work it isn't
very convenient. Obviously tab-completion of paths/programs/arguments doesn't
work since Hy's REPL doesn't do those things (by default anyway). The way
python-sh's imports work is also a bit inconvenient; either you need to
explicitly import the command you want to run, or you need to prepend it with
'sh.' ( _`sh.git`_ runs git for example).

A custom REPL might be a good way to resolve these issues, I'm going to have
to put more work into understanding how both of these things work first
though. Nevertheless, I think the idea has promise.

------
etiam
For a moment there I was a bit sad because I thought it had been edited away,
but the rampant puns in the charming Quickstart are still around.
[http://docs.hylang.org/en/latest/quickstart.html](http://docs.hylang.org/en/latest/quickstart.html)

Hyrray!

------
yason
I love Hy and I've started using it several times but it still somehow has a
few quirks that I bump into when starting to write serious code. Now it tries
to reflect how Python works which isn't always that lispy: basically I'm
waiting for more Clojure to creep in :)

~~~
Foxboron
Well, that most likely won't change. Hy's goal is to be bidirectional, so
there are a few things here and there that doesn't feel as lispy as it should.
Like having data returned when you use assoc as an example. The scoping is an
issue aswell, like let-over-lambdas doesn't work, and iteration over maps
isn't as fluid as it is in Clojure.

------
omaranto
They should implement Python's list comprehensions in full. Right now Hy only
allows one generator clause and one if clause, something like

    
    
        (list-comp (, x y) (x (range 5)) (> x 2) (y (range 2)))
    

gives an error about list_comp only taking 3 or 4 arguments.

~~~
Foxboron
After a quick IRC discussion; you wrote it wrong :D! Using hy --spy so you can
see the equivalent python code:

    
    
        => (list-comp (, x y) [x (range 5) y (range 5)] (> 2 x))   
        from hy.core.language import range   
        [(x, y) for x in range(5) for y in range(5) if (2 > x)]    
        [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (1, 1) .....]
    
    

Can submit a PR and fix the docs so this is better displayed!

~~~
omaranto
Doesn't what you wrote work in a different, slower, way?

I had in mind something analogous to

    
    
        r = []
        for x in range(5):
          if 2>x:
            for y in range(5):
              r.append((x,y))
    

I think what you wrote probably does:

    
    
        r = []
        for x in range(5):
          for y in range(5):
            if 2>x:
              r.append((x,y))
    

The first loop completely skips looping over y if the condition on x fails,
the second loop repeatedly tests x.

~~~
Foxboron
Hurm yes indeed. Interesting problem. I'll copypasta this and fix up an issue!
Nice catch!

------
diadara
[https://github.com/hylang/hy/blob/master/docs/contrib/loop.r...](https://github.com/hylang/hy/blob/master/docs/contrib/loop.rst)

I thought this is not possible in python. Can someone explain ? Python doesn't
have tail call optimization and hy produces python AST. Am I missing something
?

I tried reading through the source, but I am still a lisp noob.
[https://github.com/hylang/hy/blob/master/hy/contrib/loop.hy](https://github.com/hylang/hy/blob/master/hy/contrib/loop.hy)

~~~
Foxboron
The entry is misleading. We don't really do TCO. It's just a trampoline
calling the loop/recur form until there is nothing left to produce.

Same solution clojure has, as the JVM dosn't allow for TCO.

------
danabramov
It feels strangely.. nice.

I was always turned off by Python syntax, but liked the explicitness of Python
code. I always liked LISP “syntax” but never learned any LISPs, and Clojure is
really dense/implicit IMO. So Hy has a nice and unexpected balance of
explicitness/familiarity and aesthetics.

But I wonder why someone would prefer this, aside from aesthetics. Macros?

~~~
elarkin
Can you give an example of where you find Clojure to be implicit?

I don't find the Clojure that I write to be implicit.

~~~
danabramov
Clojure(Script) reads Rubyish to me in the sense that I can't read the code
until I understand every primitive. Contrast this with Python which almost
reads like a natural language (which I don't say is _better_ , but more
explicit IMO).

    
    
        (defn widget-c [data owner]
          (reify
            om/InitState
            (init-state [_]
              {:message nil})
            om/IDidMount
            (did-mount [_]
              (let [events (sub (:notif-chan (om/get-shared owner)) :hello (chan))]
                (go
                  (loop [e (<! events)]
                    (om/set-state! owner :message (:data e))
                    (recur (<! events)))))))
            om/IRenderState
            (render-state [_ {:keys [message]}]
              (if message
                (dom/p nil message)
                (dom/p nil "Waiting ... waiting ... waiting ..."))))

~~~
emidln
There is nothing implicit about that. You are instantiating an anonymous class
(that's the reify) implementing 3 interfaces (presumably you looked these up
since they're user libraries) that you are defining inline.

Compare this to something like a Django Rest Framework:

    
    
        class SnippetList(APIView):
            """
            List all snippets, or create a new snippet.
            """
            def get(self, request, format=None):
                snippets = Snippet.objects.all()
                serializer = SnippetSerializer(snippets, many=True)
                return Response(serializer.data)
    
            def post(self, request, format=None):
                serializer = SnippetSerializer(data=request.data)
                if serializer.is_valid():
                    serializer.save()
                    return Response(serializer.data, status=status.HTTP_201_CREATED)
                return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    

reify doesn't require any special knowledge that class doesn't. The Python
code here is subclassing a single abstract class (APIView) instead of 3, but
it has the same "problem" in that you have to know what methods to override.
Again this is user code so you probably looked up what APIView needs to work
just like you looked up with InitState, IDidMount, and IRenderState.

reify vs class in Clojure vs Python is a matter of idioms. You can definitely
create an actual class instead of reifying in clojure, it just isn't
necessary. Likewise, you could create an anonymous type in python via type(),
but you'd probably get fired and/or shot in most circles for doing so.

~~~
danabramov
I mostly wasn't talking about the high-level structure. Compare these methods:

    
    
            serializer = SnippetSerializer(data=request.data)
                if serializer.is_valid():
                    serializer.save()
                    return Response(serializer.data, status=status.HTTP_201_CREATED)
                return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    

with this:

    
    
            (let [events (sub (:notif-chan (om/get-shared owner)) :hello (chan))]
                (go
                  (loop [e (<! events)]
                    (om/set-state! owner :message (:data e))
                    (recur (<! events)))))))
    

In order to have a remote idea what's going on, I need to know about channels,
what `events`, `sub`, `go`, `loop`, `recur`, `<!` are. In Python, it's easy to
tell syntax and primitives from library classes and methods, but here I'm not
sure which is which. That's what I mean by Clojure being dense. _Of course_
it's stupid to assume you read the source without understanding the language
first, but “Python after you know some OOP“ is still easier to read for
absolute language beginner than “Clojure if you know some FP”. Again, I don't
imply that Python is somehow better because of that.

------
javaistheworst
Excellent - will have a play with this tonight. Docs look pretty good too,
only thing that stands out right now which I want to dig into a bit more is
one of the special forms, specifically the dot one - looks like something that
would be tricky to see in an eyeball scan of code, but probably worrying
prematurely.

------
divs1210
Very cool! I switched from python To clojure sometime back, but Hy looks like
something I _have_ To give a try.

------
volent
Flask apps look so great with Hy :

    
    
        (import [flask [Flask]])
        (def app (Flask __name__))
        (with-decorator (app.route "/")
          (defn index []
            "Hello World !"))
        (app.run)
    

I'm really looking forward using it in some side-projects.

~~~
Foxboron
It dosn't stop there. Why use the decorators directly when you can get Hy on
Meth?
[https://github.com/hylang/hy/blob/master/hy/contrib/meth.hy](https://github.com/hylang/hy/blob/master/hy/contrib/meth.hy)

~~~
rcarmo
Hmmm. Cute. I can probably drop those straight into my Bottle stuff.

------
blaze33
I discovered Hy two weeks ago through this previous discussion: How Hy
backported “yield from” to Python 2
[https://news.ycombinator.com/item?id=8641126](https://news.ycombinator.com/item?id=8641126)

------
auvi
I am just loving it! I code my data analysis stuff in Python and I've
accumulated a significant amount of code written in it. Is there any Py->Hy
translator out there?

~~~
rcarmo
You don't need to translate it -- you can import modules both ways just fine
-- THAT's the killer feature.

------
kelvin0
But wy?

~~~
Foxboron
Helluw, one of the core devs here. This was originally an idea just to display
whats possible with the Python AST. It's pretty neat. The result is an kinda
fancy lisp variant with bidirectional interop, macros and all the fancy lisp
stuff.

~~~
gknoy
Is this something that can be integrated easily with existing Python code? I'd
be interested in implementing a small module in Hy, rather than Python, and
then include/use/consume it from Python.

All the uses I saw listed on the blog were as a replacement for Python, it
seemed like, so this might be a direction of integration that wasn't intended.

edit: Perhaps I should read more and ask less. It looks like this is what
`import_file_to_module` does, right? :-)

~~~
paultag
Hy core here!

It works both ways, and bi-directional interop is intended! Just `import hy`
then you can import any `.hy` on `sys.path` :D

(yes, this means you can make Hy code pip installable, just like Hy itself!)

~~~
gknoy
Wow. That sounds amazing!

------
MrMan
Does it work with cython?

~~~
rcarmo
It will import cython stuff, and vice-versa.

------
sebastianconcpt
Sweet!

