Hy(lang) is a LISP-family programming language built on top of Python. You get the rich Python language & library ecosystem, with a LISP syntax and many of the language conveniences of Clojure, such as reader macros for easy access to built in data types. What's not to love?
I recently found out I have the most GitHub stars for projects written in Hy of any developer worldwide. With this admittedly ridiculous credential in hand I'd like to offer some opinions on the language.
I really like Hy a lot. I prefer writing code in Hy to writing code in Python, and I am writing this post because I want to see Hy do well. I originally wrote this as a list of things in Hy that could possibly be improved, from the perspective of an end user such as myself. Then my friend Crispin sent me this fantastic video by Adam Harvey: "What PHP learned from Python" and I realised that all of the issues I have with Hy stem from the same basic problem that Adam talks about.
Here is the crux of the problem: I've written a bunch of software in Hy over the past few years and often when I moved to a newer point-release of the language all my old code breaks. My hard drive is littered with projects which only run under a specific sub-version of Hy.
gilch[m] It might depend on the Hy version you're using.
I understand that the maintainers of the language, who are hard-working people doing a public service for which I'm deeply grateful, are concerned with making the language pure and clean and good. I understand that languages have to change to get better and they need to "move fast and break things" sometimes. That's all fine and good.
However, I think Adam Harvey's point stands. If you want users to use and love your language:
- Break things cautiously
- Maintain terrible things if it makes life better
- Expand the zone of overlap [backwards compatibility between consecutive versions]
I think if you can maintain backwards compatability you should.
Almost all of the breakages I've experienced between Hy versions could have been avoided with aliasing and documentation. Not doing this backwards compatibility work basically tells your users "don't build things with this language, we don't care about you." I know that is almost certainly not the attitude of the maintainers (who are lovely, helpful people in my experience) but it is the way it comes across as an end user.
Here is a list of things which have changed between versions which blew up my Hy codebases each time I upgraded Hy:
-
Renaming
true
,false
, andnil
to the Python nativesTrue
,False
, andNone
. These could have been aliased to maintain backwards compabitility. -
Removing
list-comp
anddict-comp
in favour of the excellentlfor
,dfor
, and friends. Again, small macros could have aliased the old versions to the new versions. -
Removing
def
in favour ofsetv
. A very small macro or maybe even an alias could have retaineddef
as it is pretty much functionally identical from the perspective of an end user. -
Removing
apply
, presumably in favour of#**
. Support could have been retained for the functionalapply
. There are situations where a properapply
is favourable. -
Removing the core macro
let
. It seems there was an issue wherelet
would not behave correctly with generators and Python'syield
statement. A more user-friendly solution than chopping the imperfectlet
from the default namespace and breaking everybody's code would have been to document the issue clearly for users and leave the imperfectlet
in. I know it is available in contrib. Moving it to contrib broke codebases.
Having your old code break each time you use a new version is frustrating. It makes it hard to justify using the language for new projects because the maintainance burden will be hy-er (sorry heh).
Some other minor nits I should mention which I think would vastly improve the language:
-
loop
/recur
should be core. Likelet
these are available in contrib, but that means you have to explicitly import them. Additionally it would be super nice if they were updated to support the vector-of-pairs declaration style oflet
andcond
etc. -
Why does
assoc
returnNone
? This is completely unexpected. If there are performance issues then create an alternative which does what Python dict assignment does (aset
?) but it seems unwise to break user expectations so fundamentally.
It is much easier to provide criticism than it is to write working code. I am sorry this is a blog post instead of a pull request, and I hope this criticism is seen as constructive. I want to thank everybody who has worked on hy
. I am a huge fan of the language. It is an amazing piece of software and I am very grateful for your work.