Squid Lisp written in Python: Hy

This is the 7th day article of iRidge Advent Calendar.

This article introduces Hy, a Lisp dialect implemented in Python.

Motivation

I'm tanaka.lisp, a server-side engineer at iRidge Inc. I want to see the world of the web and Python, and I work for the Python company IRIDGE.

Suddenly, the language I love so much is Common Lisp. In my business, I read and write Python exclusively, but since Python is not Lisp, I am at the mercy of automatic indentation, and ʻif pred1 and pred2: ...ʻif (and pred1 pred2): ... I'm constantly struggling to write. Lisper-specific withdrawal symptoms may also appear. I started thinking about sexp, googled with "Hyperspec function name ", and started typing sexp into Emacs. In the end, it's an incomprehensible idea that __Python should become Lisp, which crosses my head during business hours.

Oh, if Python was an S-expression. If you could write an S-expression while riding on the huge standard library group of Python like Language. Oh, oh.

But there is. Such a dreamy, squid language.

Hy

Hy is a Lisp dialect written in Python.

It has a syntax that is strongly influenced by Clojure, and if you get lost in the Hy Style Guide, the order is Python> Clojure> Common Lisp. So, follow the conventions of that language.

Furthermore, it is possible to interop with Python like Clojure, and it seems that Django application can be written in Hy. Oh, isn't Hyde good with this anymore? ??

By the way, Haskell is famous as a squid language, but Hy's mascot seems to be squid. You're squid. The documentation Three Minutes Tutorial is the most interesting and I recommend you to read it. I was knocked out with this.

Hy installation

Let's install it. Here, install it in the virtualenv environment [^ 1].

[^ 1]: By the way, it seems that it is registered as python-hy in ubuntu's apt repository.

First, create a venv environment,

$ virtualenv venv
# ...Lots of output
$ cd venv
$ source ./venv/bin/activate

Then install it from github with pip.

(venv) $ pip install git+https://github.com/hylang/hy.git
# ...Lots of output

Hello to start the REPL is after.

(venv) $ hy
hy 0.11.0+320.g5b87932 using CPython(default) 2.7.12 on Linux
=> (print "Hy!")
Hy!
=>

It ’s easy, is n’t it?

Hy Hitomi Tour

Let's see what kind of language it is.

Basic syntax

data structure

;;;Numerical value and truth value
=> 42
42L
=> False
False
;;;String
=> "forty two"
u'forty two'

First of all, the data structure that can be used in Python can be used almost as it is. However, it seems that writing , or : or delimiters is prohibited.

;;;list. `,`Do not write
=> ["life" "universe" "everything"]
[u'life', u'universe', u'everything']
;;;dictionary.This also does not require a delimiter
=> {"arthor" "dent" "ford" "prefect"}
{u'arthor': u'dent', u'ford': u'prefect'}

And here are the Lisp-specific data types. It seems strange that the symbol display is a Unicode character string.

;;;symbol.The display is a character string, but the type is a symbol properly
=> 'marvin
u'marvin'
=> (type 'marvin)
<class 'hy.models.symbol.HySymbol'>
;;keyword
=> :deep-thought
u'\ufdd0:deep-thought'
;;Symbols that are always unequal to others
=> (gensym)
u':G_1236'

And how, there is no nil! Ah, God, how do I write an empty list?

;;;no nil!
=> nil
Traceback (most recent call last):
  File "<input>", line 1, in <module>
NameError: name 'nil' is not defined

God "You should use()!" I "I see!"

=> ()
[]

In fact, it seems that even Lisp's traditional list notation may ** sometimes become a Python list **.

=> '(a b c)
(u'a' u'b' u'c')
=> (cons 'googolplex (cons 'star (cons 'thinker ())))
[u'googolplex', u'star', u'thinker']

And cons. It seems that Lisp's father John McCarthy pronounced Corn Snake.

=> (cons 'arther 'dent)
(u'arther' . u'dent')
;;;It is OK to write in dot pairs and quote
=> '(arther . dent)
(u'arther' . u'dent')

function

Since Hy is a Lisp language, function calls are written in Polish notation.

;;;String concatenation
=> (+ "forty" " " "two")
u'forty two'
;;;Four arithmetic operations
=> (+ 1 (* 5 8) 1)
42L

The function definition looks like Clojure. It's always the same as when I just type defun and get confused.

=> (defn factorial [n]
...   (if (<= n 0)
...       1
...       (* n (factorial (- n 1)))))
=> (factorial 10)
3628800L

By the way, the list is syntactic sugar for Cons. Lisp code is written in a list. So you should be able to write the code in chords. In Common Lisp, even if you write a program in dot pairs, it will be read properly, but ...

=> (print . ("forty" . ("two" . ())))
forty two

Oh! I'll do it! This paved the way for obfuscation.

Access Python functions

As an example of using Python's rich standard library, let's try Examples of communication using httplib with Hy.

;;;import.from can also be imported
=> (import httplib)

=> (setv conn (httplib.HTTPSConnection "www.python.org"))
=> (conn.request "GET" "/")
=> (setv res (conn.getresponse))
=> (print res.status res.reason)
200 OK

Just read it as an S-expression and it's done. This is your chance to migrate your scripts written in Python to Hy.

macro

Now, make sure Hy isn't just familiar with Python, it's also a Lisp companion. For those who aren't Lisper, macros here aren't C macros, they're the ones that manipulate the abstract syntax tree. Rust, Nim, Scala, etc. have this feature. By the way, Hy [already has an anaphoric macro] as a contributor module [http://docs.hylang.org/en/latest/contrib/anaphoric.html).

As an example, for the time being, I thought that anaphoric macros are appropriate because the benefits are easy to understand. An anaphoric macro, for example, allows you to refer to the value of an expression in the conditional part of an if statement from behind. For details, see [Corresponding Chapter of On Lisp](http://www.asahi-net. Please also read or.jp/~kc7k-nd/onlispjhtml/anaphoricMacros.html).

For the time being, I will implement it by copying from On Lisp and use it.

=> (defmacro aif [test-form then-form &optional else-form]
...  `(let [it ~test-form]
...     (if it ~then-form ,else-form)))
=> (aif (+ 1 2)
...  (print 'cheese it it))
cheese 3 3

You can refer to the evaluation result of the conditional expression of the if expression (here, ʻaif) in ʻit. Let's see how the expression is converted

;;;The output is hand-formatted for easy viewing
=> (macroexpand '(aif (+ 1 2)
...                (print 'cheese it it)))
((u'fn' [](u'setv' u'it' (u'+' 1L 2L))
    (u'if' u'it'
        (u'print' (u'quote' u'cheese') u'it' u'it')
        u',else_form')))

The symbol is displayed as a Unicode string, which is awkward, but it looks good.

What makes me happy about this

--Already calculated value (when conditional branching) can be used --You don't have to write the same conditional expression over and over again

That is a nice point.

As an example, consider the case where a conditional expression is an expression that takes a lot of time.

=> (reduce (fn [a b](+ a b)) (range 1 1000000000))
499999999500000000

When I hit this formula for the time being, the REPL was silent for about a minute: sob: **. When you want to use this result many times

(aif (reduce (fn [a b](+ a b)) (range 1 1000000000))
  (print (+ it it))

It is a dimension that can avoid multiple calculations if it is set as such. It is a story that you should assign it to a local variable, but if you write an anaphoric macro, the creation and assignment of the local variable will be done automatically behind the scenes.

Access Python from Hy

Finally. You can convert Hy code to Python code. By doing this, you can eliminate the danger of Hy that is held by REPL ~ ~ when developing, write in Hy and convert to Python when committing ~ ~.

If you try it, the overhead of actually parsing will be reduced, or there will be no lag until the result is returned.

(venv) $ cat upto.hy
(defn up-to-n [n]
  (list-comp n (n (range n)) (= (% n 2) 0)))

(print (up-to-n (integer (raw-input "input number: "))))

# upto.Convert hy
(venv) $ hy2py upto.hy > upto.py

#Conversion result
(venv) $ cat upto.py
from hy.core.language import integer, range

def up_to_n(n):
    return [n for n in range(n) if ((n % 2L) == 0L)]
print(up_to_n(integer(raw_input(u'input number: '))))
(venv) $ python upto.py
input number: 10
[0, 2, 4, 6, 8]

Summary

Introduced the Lisp dialect Hy written in Python.

--Hy installation is easy with pip --Hy syntax is simple, convenient, and S-expression --Hy can access Python assets --Hy has Lisp metaprogramming power --Hy code can be converted to Python code to make it faster

It is an impression that I touched and wrote that it is a fairly well-made language.

This article was originally intended to be published a year ago [^ 2]. However, after all, when I got to know the world of Python and touched it, I could guess the background of Hy's way of doing things. Perhaps it was the right time to get used to Python.

[^ 2]: I was thinking of announcing it in the in-house LT, but it seems difficult to do it in this article LT ...

I think Hy itself is a pretty good language, so Lisper brothers who are engaged in the Python industry consider introducing it when they say, "I have to use Python, but the syntax is not an S-expression." How about trying it?

Recommended Posts

Squid Lisp written in Python: Hy
Gacha written in Python -BOX gacha-
Compatibility diagnosis program written in python
Simple gacha logic written in Python
Fourier series verification code written in Python
Stress Test with Locust written in Python
Markov chain transition probability written in Python
Quadtree in Python --2
Python in optimization
CURL in python
Metaprogramming in Python
Python 3.3 in Anaconda
Geocoding in python
SendKeys in Python
Meta-analysis in Python
Unittest in python
Epoch in Python
Discord in Python
Sudoku in Python
DCI in Python
quicksort in python
nCr in python
N-Gram in Python
Programming in python
Plink in Python
Constant in python
Lifegame in Python.
FizzBuzz in Python
Sqlite in python
StepAIC in Python
N-gram in python
LINE-Bot [0] in Python
Csv in python
Disassemble in Python
Reflection in Python
Constant in python
nCr in Python.
format in python
Scons in Python3
Puyo Puyo in python
python in virtualenv
PPAP in Python
Quad-tree in Python
Reflection in Python
Chemistry in Python
Hashable in python
DirectLiNGAM in Python
LiNGAM in Python
Flatten in python
flatten in python
Introduction to effectiveness verification Chapter 3 written in Python
Introduction to Effectiveness Verification Chapter 2 Written in Python
Sorted list in Python
Daily AtCoder # 36 in Python
Daily AtCoder # 2 in Python
Implement Enigma in python
Daily AtCoder # 32 in Python
Daily AtCoder # 18 in Python
Edit fonts in Python
Singleton pattern in Python
File operations in Python