Hy at a Glance — A Language a Day, Advent Calendar 2019 Day 18/24

About this ‘A Language a Day’ Advent Calendar 2019

Welcome to Day 18 of this year’s A Language a Day Advent Calendar. Today’s topic is introduction to the Hy programming language.

Facts about the language

  • A dialect of List
  • Basically, it is Python written with the syntax of Lisp
  • Appeared in 2013
  • Website: hylang.org

Installing and running Hy

To install Hy, you need to have Python installed first. Then, you simply install a package using pip:

$ pip install git+https://github.com/hylang/hy.git

To run a program, use the hy command-line tool:

$ hy hello-world.hy 

Hy is an interesting language that can help in introducing you to the world of Lisp-like languages. If you are familiar with Python, you can concentrate on re-thinking Python programs as if they would be written in Lisp with all that ‘inverted’ syntax and lots of parentheses.

Hello, World!

Here is a simple Hello, World! program in Hy:

(print "Hello, World!")

Looking at the Python code

The --spy command-line option allows you to see how your program is being transformed:

$ hy --spy < hello-world.hy 

In the output, you can see the equivalent Python code:

=> print('Hello, World!')

Variables

To create (or to bind) a variable, use the setv built-in feature.

(setv username "John")
(print (+ "Hello, " username "!"))

In this example, you can also see how to concatenate strings, and on a broader scale, how to use operators: instead of a + b, you type (+ a b).

Functions

Functions are created with the defn keyword. It is followed by the function name and the function arguments in square brackets:

(defn greet [username]
    (print (+ "Hello, " username "!")))

(greet "John")

Calling a function is done in a separate pair of parentheses, similarly to how we already invoked print.

Calling a method

There are two ways of calling a method on an object. For example, in the following code, the Python’s str.upper() method is called on a variable. You can use either syntax:

(setv greeting "hello")

(print (greeting.upper))
(print (.upper greeting))

A Factorial example

I hope that at this moment, you can write a factorial function yourself. Here is my solution:

(defn factorial [n]    
    (if (< n 2)
        1
        (* n (factorial (- n 1)))))

(print (factorial 1)) ; 1
(print (factorial 5)) ; 120
(print (factorial 7)) ; 5040

Take in mind that you do not need a return statement to return a value from the function. The expressions such as n < 2 or n - 1 are spelled as (< n 2) and (- n 1) in Hy.

Classes

Let us see how you define classes in Hy with the defclass keyword. Here is a class for keeping the name and the age of a person:

(defclass Person [object]  
  (defn --init-- [self name age]
    (setv self.name name)
    (setv self.age age))

  (defn info [self]
    (+ self.name " is " (str self.age) " y.o.")))

(setv john (Person "John" 30))
(print (.info john)) ; John is 30 y.o.

(setv alla (Person "Alla" 20))
(print (.info alla)) ; Alla is 20 y.o.

Notice how Hy transforms hyphens in identifier names to underscores. In a similar manner, you could, for example, name a method info-string and it will be trans-compiled to info_string.

A Polymorphic example

Hierarchy is Python is expressed as an argument to the class name, so in Hy, the base class is mentioned in square brackets. The full polymorphic example code is shown below.

(defclass Animal [object]
    (defn what [self]
        "it is an animal"))

(defclass Dog [Animal]
    (defn info [self]
        (+ "This is a dog and " (.what self))))

(defclass Cat [Animal]
    (defn info [self]
        (+ "This is a cat and " (.what self))))

(setv zoo [(Cat) (Dog) (Cat) (Dog)])

(for [x zoo]
  (print (.info x)))

In this program, both Dog.info and Cat.info call a method of their base class: Animal.what. The program prints the following output:

This is a cat and it is an animal
This is a dog and it is an animal
This is a cat and it is an animal
This is a dog and it is an animal

Using modules

To implement the next program we need to import a module, so let us see how you do it with Hy:

(import datetime)

(print (.now datetime.datetime))

Sleep sort

For the reference, here is a Python program that implements the Sleep sort algorithm. For simplicity, a number of intermediate variables are introduced. In real code, this may not be needed, but it is very handy for converting to Hy.

import time
import threading
 
data = [10, 4, 2, 6, 2, 7, 1, 3]
for n in data:
    delay = n / 10
    timer = threading.Timer(delay, print, [n])
    timer.start()

time.sleep(2)

And here is the corresponding program in Hy:

(import time)
(import threading)
 
(setv data [10, 4, 2, 6, 2, 7, 1, 3])

(for [n data]
    (setv delay (/ n 10))
    (setv timer (threading.Timer delay print [n]))
    (.start timer))

(.sleep time 2)

Resourses

There are not that many resources outside of the Hy website and the corresponding GitHub repository. Nevertheless, this language is a great thing to try before learning any other Lisp-like language.

You can find source codes of the examples shown in this article in my repository on GitHub.

Next: Day 19. Red

One thought on “Hy at a Glance — A Language a Day, Advent Calendar 2019 Day 18/24”

Leave a Reply

Your email address will not be published. Required fields are marked *