1
\$\begingroup\$

I think I'd like to learn Clojure eventually, but at the moment am having fun with Hy, a "dialect of Lisp that's embedded in Python."

(import socket)

(defn echo-upper (sock)
  (.send c (.upper (.recv c 10000)))
  (.close c)
)

(def s socket.socket)
(.bind s (tuple ["" 8000]))
(.listen s 5)
(while True
  (echo-upper (.__getitem__ (s.accept) 0))
)

Python generated from ABS with astor:

import socket

def echo_upper(sock):
    c.send(c.recv(10000).upper())
    return c.close()
s = socket.socket
s.bind(tuple([u'', 8000]))
s.listen(5)
while True:
    echo_upper(s.accept().__getitem__(0))

I'm not worried about not dealing with connections properly etc, I'd just like feedback on using lispy syntax. I'm tempted to do things like ((getattr s "bind") (tuple ["" 8000])) because it looks more like simple Scheme, the only lisp I have a little experience with.

Any thoughts on syntax and style? Any guidelines for dealing with Python APIs in a lispy way, something that apparently happens a lot in Clojure with Java?

\$\endgroup\$

4 Answers 4

1
\$\begingroup\$

I wouldn't use the threading macro here. That's most useful when you want to see the data flow, but you're using c two times in this expression, there's no clear flow that -> could make easier to untangle. It makes it more complicated in this case...

If you'd decouple the receive and the send part, then it'd be different, but then the expression becomes so simple you don't even need the macro:

(let [[received (.recv c 10000)]]
  (.send c (.upper received)))

With the threading macro, the inner part would become (-> received (.upper) (.send c)) - doesn't make it much clearer, either.

What you could do, however, is abstract away the accept -> do something -> close thing into a macro:

(defmacro with-accepted-connection [listening-socket & rest]
  (quasiquote (let [[*sock* (get (.accept (unquote listening-socket)) 0)]]
                (unquote-splice rest)
                (.close *sock*))))

(with-accepted-connection s (.send *sock* (upper (.recv *sock* 10000))))

The above may or may not work out of the box, I just conjured it up. But something similar may make the code lispier and easier to understand the intent later.

\$\endgroup\$
1
\$\begingroup\$

So, yeah, the threading macro is a great way of writing it - using it is a borrowed bit of syntax from Clojure :)

FYI, using a tupple in Hy is:

(, 1 2 3)
; (1, 2, 3)

If you'd like, I can help you through another version of this in a more "Hythonic" way (like Algernon's!)

\$\endgroup\$
0
\$\begingroup\$

As suggested by the Hy docs, I'm trying the threading macro here:

(.send c (.upper (.recv c 10000)))

becomes

(-> (.recv c 10000) (.upper) (c.send))

I'm not sure I like it.

\$\endgroup\$
0
\$\begingroup\$

algernon in freenode/#hy says:

Looks lispy enough to me, except for the __getitem__ line.

...and the closing braces.

I'm think this is fix for the braces and using get instead of __getitem__:

(import socket)

(defn echo-upper (sock)
  (.send c (.upper (.recv c 10000)))
  (.close c))

(def s socket.socket)
(.bind s (tuple ["" 8000]))
(.listen s 5)
(while True
  (echo-upper (get (s.accept) 0)))
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.