Hy – A dialect of Lisp that’s embedded in Python
docs.hylang.org
docs.hylang.org
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).
Here's a wiki implemented with it (demonstrates using Bottle, doing URL routing and few other things): http://github.com/rcarmo/sushy ...and here's a little playground with various snippets of code: https://github.com/rcarmo/hy-there
...and a feed fetcher: https://github.com/rcarmo/mqtt-feed-fetcher/blob/master/main...
...and a prototype for a nicer REPL: 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)
http://nathanw.net/2014/12/04/using-hy-a-dialect-of-lisp-for...
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...
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'))
)
)
)),
/* ... */
)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.
If you are a Python enthusiast, lisp-like languages can be used to write DSLs or cut down on boilerplate code in a way which isn't as easy in Python or similar languages.
But yes, in general, a lisp dialect on Python has the problem that Python itself is a great language.
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).
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.
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.
What stands out will be different for different people. I use the ftp and telnet libs a lot, I've used the imap and email libs.
Here's a repackaging of a large number of 3rd party libs, packaged as an alternative python distribution, which I use at work: http://docs.continuum.io/anaconda/pkg-docs.html
I've explored twisted (networking), numpy and scipy (math and scientific), and sympy (symbolic math).
IPython's shell and notebook are great work environment enhancers. http://ipython.org/
Here's Python's larger set of 3rd parties: https://pypi.python.org/pypi?%3Aaction=browse
(list-comp (, x y) (x (range 5)) (> x 2) (y (range 2)))
gives an error about list_comp only taking 3 or 4 arguments. => (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!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. (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.
In many languages, and does not evaluate all of its arguments, only as many as it needs to until it finds #f. This sounds like an easy thing to implement, but in most languages it actually isn't, because order of nested operations means that without being asked, it will evaluate it's arguments before our and.
So we can write this broken-and easily enough:
(define (broken-and . rest)
(cond
[(null? rest) #t]
[(car rest) (apply broken-and (cdr rest))]
[else #f]))
But give it the following, and you get an error: > (broken-and (zero? 0) (string? "dave") (null? '(is fat)) (/ 5 0))
. . /: division by zero
And that will work so long as all your args are valid. But what will work better is to write a macro. Macros aren't evaluated, they merely splice in their syntax, but we can write them a lot like code, using all our control structures and so forth, and in Scheme/Racket, also some cool pattern matching stuff): (define-syntax my-and.v2
(syntax-rules ()
[(_) #t]
[(_ a) a]
[(_ a b ...) (if a
(my-and.v2 b ...)
#f)]))
Now we can try our input from before, and get what we want, because the macro expands recursively and won't ever get to our divide by zero, at least unless all preceding args are true: > (my-and.v2 (zero? 0) (string? "dave") (null? '(is fat)) (/ 5 0))
#f
This property of only expanding, not evaluating, is why macros can write all kinds of clever control structures, because you're mangling syntax, not evaluating code.Macros in general work by being a syntax tree -> syntax tree transformation. The neat trick is it's not a static pattern like C - you can leverage functions, conditions, etc to build that syntax tree.
As for comprehensions themselves, if you want to see the power of lisp look at the loop macro. It's a single macro yet gives you the power of normal control loops, list comprehensions, for-each all rolled into one package. [3]
[1] http://data-sorcery.org/2010/05/14/infix-math/
[2] http://www.gigamonkeys.com/book/loop-for-black-belts.html
[3] http://stackoverflow.com/questions/267862/what-makes-lisp-ma...
It is a little more involved, but basically you define a syntax for list comprehension and then parse it using the macro to generate the final result.
We implement yield-from on Python2 using a macro. http://dustycloud.org/blog/how-hy-backported-yield-from-to-p...
Anything above that is written as Hy macros, in Hy itself: https://github.com/hylang/hy/blob/master/hy/core/macros.hy
There's a lot of other core code that's written in Hy itself too. https://github.com/hylang/hy/tree/master/hy/core
The implementation is easier if you assume that you have exactly two generators. Here is an example in Racket. It uses define-syntax-rule that is the method to define simple macros (there are more advanced options).
(The program also define string->list/string that splits a list in a list of one-character string, because the result of string->list is a list of chars. You can ignore it safety.)
#lang racket
(define (string->list/string x)
(map (lambda (c) (list->string (list c)))
(string->list x)))
(define-syntax-rule (list-comp2 (proc v ...)
[u1 gen1]
[u2 gen2])
(map (lambda (u1 u2) (proc v ...))
gen1
gen2))
(list-comp2 (list x y)
[x (range 8)]
[y (string->list/string "ABCDEFGH")])
;==> '((0 "A") (1 "B") (2 "C") (3 "D") (4 "E") (5 "F") (6 "G") (7 "H"))I like to compare the way lisp works to Javascript and the DOM. Just like a webpage (or its JS code) has access to its internal structure and can do arbitrary transformations, so does lisp except instead of the DOM you have the abstract syntax tree.
http://www.ai.sri.com/~latendre/listCompFinal.pdf
I have a (very loose) implementation of this approach at:
https://github.com/TBRSS/cl-lc
Frankly, though, once you've learned the trick, list comprehensions lose a lot of their appeal.
For instance, I did it once in a "researchy" way by implementing monads. With the help of CLOS, I was able to define various monads such as the list monad, identity monad, or state transformer monad (the map, join and unit being methods dispatched on the monad type).
Then a generic monadic comprehension macro provides different functionality based on which monad is selected. The list monad gives rise to a list comprehension. The identity monad gives rise to just sequential variable binding. And the state transformer monad ... to something like a pipeline of succcessive state transformations, I think.
http://www.kylheku.com/cgit/lisp-snippets/tree/monads.lisp
If you just want a list comprehension, it's not very difficult. Basically it has to expand to code which (for example) expresses a nested loop that steps every variable over its respective list. The expression being collected by the comprehension is stuck into the middle of this loop, inside a piece of code which collects its value into a list, which is stored in a hidden local variable. (Perhaps two local variables are used, to keep track of the head and tail of the list for the sake of efficiency.)
ANSI Lisp has functional applicators that process multiple lists. For instance if you want to add together values from two lists, you can do this:
(mapcar '+ '(1 2 3) '(10 20 30))
-> (11 22 33)
If you wanted to form a cross product of the two lists instead, the obvious thing would be to write a mapcar-like function: (mapcar-cross '+ '(1 2 3) '(10 20 30))
-> '(11 21 31 12 22 32 13 23 33)
This function could be used as the target syntax for a comprehension macro so that, say: (list-comprehend (+ b a) (a '(1 2 3)) (b '(10 20 30)))
-> '(11 21 31 12 22 32 13 23 33)
is macro-expanded into (mapcar-cross (lambda (a b) (+ b a)) '(1 2 3) '(10 20 30))
Translating (transliterating, really) the list-comprehend syntax into mapcar-cross is not very difficult: it's just some very straightforward nested list manipulation. The macro has to collect the list of variables from the trailing arguments after the expression, to form the (a b) argument list of the lambda. It has to remove the variables from those trailing arguments to produce the list expressions like '(1 2 3) and '(10 20 30) that will be applied to mapcar-cross. Then it has to assemble the mapcar-cross syntax.Of course, you also have to write mapcar-cross, which is your "run-time support function" for your list-comprehend syntax (usable directly without that syntax, too).
[0]: http://lisp-univ-etc.blogspot.com/2013/01/real-list-comprehe...
A normal Lisp function call might look like this:
(myfunc arg1 arg2 arg3)
where arg1, etc can be literals, variables, expressions. Like in most programming languages, each of the arguments is evaluated and then the function is called with the evaluated values of those arguments. When it's done, a value is returned.
A macro is a lot like a function call:
(mymacro arg1 arg2 arg3)
However, it works differently. First of all, the arguments are passed straight into the macro - if arg1 is something like (+ 1 2), the macro will see (+ 1 2), not 3 (as the function would).
Secondly, the macro is intended to return a list, which can be a function call or another macro call. (The macro could also return a final value.) The list can then be evaluated to either expand another macro, or to call a function to produce a final value. So you can think of a macro as a special sort of function that, rather than taking in values to produce another value, takes in literal code to produce transformed code (which then can be evaluated to produce a final value).
Now, one of the uses of macros that people cite is, for example, implementing your own control flow structures. Why are the two related? Lisps have something which are known as special forms, which look like function calls but don't have their arguments evaluated before being passed in (like how macros work). The reason that matters can be described by a simple example: let's say you wanted to implement a very simple version of 'and' as a function (Clojure syntax):
(defn my-and [left-expr right-expr] (if (not left-expr) false (if (not right-expr) false) true))
However, the problem is that left-expr and right-expr will both be evaluated before my-and can even begin executing, along with the attendant side effects, etc. The problem is that functions can't choose whether or not their arguments should or shouldn't be evaluated - they just get the values of their arguments after they've been evaluated[1]. It's not really possible to implement the traditional short-circuiting 'and' using functions, or many control flow structures where you do not want to evaluate every possible branch unconditionally.
However, macros receive the literal code comprising their arguments, and they can choose to evaluate one, none, some, or all of their arguments depending on their internal logic. This makes them better suited for implementing behavior that resembles those of the Lisp special forms.
[1] This is the difference between call-by-name and call-by-value semantics. Most languages are call-by-value, as described above, but (e.g.) Scala has '=>' and Swift has '@autoclosure' to allow for some sort of call-by-name support.
Abandoned because the near impossibility of compilation when using them ( http://stackoverflow.com/questions/18324743/why-were-fexprs-... ).
And more recently http://web.cs.wpi.edu/~jshutt/kernel.html .
I was pointing out an approach taken by Rebol that's different from most languages that support macros that might be interesting to people, not making an argument.
- Someone who built a simplistic lisp->lua compiler a couple of years ago at https://github.com/meric/l2l
(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.Hyrray!
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
Same solution clojure has, as the JVM dosn't allow for TCO.
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?
I don't find the Clojure that I write to be implicit.
(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 ..."))))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.
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.my 2 cents.
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? :-)
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!)
Hy uses a metaimporter to parse the Hy code and hand the Python compiler AST. So you can import any hy file like normal Python and use the code. Its that simple.
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.