Hello World From Flask With Hylang (using Docker)
Tags: lisp, hylang, python, docker
Published: 2020-07-18 11:16:20

---
title: Hello World From Flask With Hylang (using Docker)
published: true
date: 2020-07-18 00:00:00 UTC
tags: lisp, hylang, python, docker
cover_image: https://images.unsplash.com/photo-1567866953028-7afde8db2876?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=420&ixid=eyJhcHBfaWQiOjF9&ixlib=rb-1.2.1&q=80&w=1000
canonical_url: https://www.rockyourcode.com/hello-world-from-flask-with-hylang-using-docker/
---

_(image by [@zorzf](https://unsplash.com/@zorzf))_

## Why Hy?

My second programming language was a Lisp. I fell in love with the expressiveness of Lisps.

But most Lisps are obscure. The ecosystem is not suited for beginners.

One of younger Lisps, [Clojure][clojure], was my first foray into the world of web-development.  
I failed spectacularly because I didn't know how web-develoment worked, and the Clojure ecosystem assumes too much previous knowledge.

Other languages, e.g., [Racket][racket], are more approachable, but are still too unpopular to have a big community.

Let's face it, Lisps are no mainstream programming languages.

Python is beginner-friendly. It has a big ecosystem suited for web development, machine-learning and more.

Enter **[Hy][hylang]**, a Lisp dialect that transforms into the Python abstract syntax tree.  
You can use all the goodness of Python, but with a Lisp.

Every Python package can be used with Hy, and vice versa.

Let's create a simple "Hello World" web app with Flask, Hy and Docker.  
(Flask is a lightweight web application framework for Python.)

## Installation

You'll need **[Docker][docker]**.  
I also use a [Makefile][make] to run the Docker commands, but that's optional.

I'm on a Linux computer. MacOs should work, too. For Windows, I recommend setting up [WSL 2][wsl2].

Create a new folder called `hy-flask` on your computer.

(All the code is available on [GitHub][hy-flask].)

## Dockerfile

Inside the `hy-flask` folder, we'll create a `Dockerfile` with the following content:

```docker
# base image
FROM python:3.8.4-buster

# create user
ARG USER_ID=1000
ENV USER_ID $USER_ID
ARG GROUP_ID=1000
ENV GROUP_ID $GROUP_ID

# add non-root user and give permissions to workdir
RUN groupadd --gid $GROUP_ID user && \
          adduser user --ingroup user --gecos '' --disabled-password --uid $USER_ID && \
          mkdir -p /usr/src/app && \
          chown -R user:user /usr/src/app

# virtualenv
ENV VIRTUAL_ENV=/opt/venv
RUN python3 -m venv $VIRTUAL_ENV
ENV PATH="$VIRTUAL_ENV/bin:$PATH"

# set working directory
WORKDIR /usr/src/app

# install system dependencies
RUN apt-get update && apt-get clean

# add and install requirements
RUN pip install --upgrade pip
COPY ./requirements.txt .
RUN pip install -r requirements.txt

# switch to non-root user
USER user

# disables lag in stdout/stderr output
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1
# Path
ENV PATH="/opt/venv/bin:$PATH"

# run
CMD ["python", "-m", "shim"]
```

We start from a Debian Python image. Then we create a non-root user.

Next, install the Python packages into an isolated [virtual environment](https://docs.python.org/3/library/venv.html).

The Docker container will start a Python program called `shim.py`. I will come back to that later.

Let's first create a `requirements.txt` file with all dependencies inside our main folder (`hy-flask`):

```python
Flask==1.1.2
hy==0.19.0
```

## Our First Flask Application

Here is [a minimal "Hello, world!" example with Python][fullstackpython]:

```python
from flask import Flask
app = Flask(__name__)


@app.route('/')
def hello_world():
    return 'Hello, World!'

if __name__ == '__main__':
    app.run()

```

Now the same with Hy.  
Create a folder called `app` within your project's directory.
Make a new file called `app.hy` (**not `.py`**).

```clojure
#!/usr/bin/env hy
(import [flask [Flask]])
(setv app (Flask "__main__"))

(with-decorator (app.route "/")
    (defn index []
        "Hello, world!"))
```

We import Flask, then we [bind the variable app with `setv`](https://docs.hylang.org/en/stable/language/api.html?highlight=setv#setv).

[`with-decorator`](https://docs.hylang.org/en/stable/language/api.html?highlight=setv#with-decorator) is used to wrap a function with another, the same as Python.

We also [define a function with `defn`](https://docs.hylang.org/en/stable/language/api.html?highlight=setv#defn). The function takes no parameters (`[]`) and returns `"Hello, world!"`.

The app won't run _yet_.

Our Docker container runs a start script called `shim.py`. The Python program is a [work-around][gh].

Why do we need that?  
We want to tell the Flask app to run on host `0.0.0.0`. Per default, [the Flask server runs on `localhost` and you cannot see it outside of your network](https://flask.palletsprojects.com/en/1.1.x/quickstart/).  
Docker creates a separate network which we can forward to outside the container. We need to run the Flask application on an externally visible server.

In Python, this would look like this:

```python
# previous code

if __name__ == '__main__':
    app.run(host=0.0.0.0)
```

Let's create the shim. Make a new Python file called `shim.py` inside the `app` folder with the following content:

```py
import hy
from app import app

if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0")
```

(This code is from [Paul Tagliamonte](https://github.com/paultag).)

## Build And Run Docker

We can now build the Docker container. Run the following command inside the terminal in the main project folder (`hy-flask`):

```bash
docker build -t hy-flask .
```

After you've build the container, you can run it:

```bash
docker run --rm -it -v "${PWD}/app":/usr/src/app -p 5000:5000 hy-flask
```

This runs an interactive (`-it`) Docker container which will be removed (`--rm`) after you've stopped it.  
The `-v` flag [binds a volume](https://docs.docker.com/storage/volumes/). For development, we mount our local `app` folder into the container.  
Now we don't have to rebuild the container even if we change the code.  
We also [publish the port 5000](https://docs.docker.com/engine/reference/commandline/run/#publish-or-expose-port--p---expose). Now we can see the app on our computer.  

Go to `http://localhost:5000` and admire your Hy Flask application!

**Optional**:  
Create a `Makefile` with the following content (inside the main folder):

```make
all:
	@echo "Usage: build or run"

build:
	docker build -t hy-flask .

run:
	docker run --rm -it -v "${PWD}/app":/usr/scr/app -p 5000:5000 hy-flask
```

You can build the container with `make build`. Run the container with `make run`.

You can see all the code on [GitHub][hy-flask].

## Done

That's it! All you need to do to get a Docker container running with Hy and Flask.

The Python shim threw a wrench into the spanner, but other than that, the translation from Python to Hy is straightforward.

## Further Reading

- [A Lisp Programmer Living in Python-Land: The Hy Programming Language][leanpub] by Mark Watson
- [What is a Makefile and how does it work?][make] (Opensource.com)
- [A Linux Dev Environment on Windows with WSL 2, Docker Desktop and More][wsl2] by Nick Janetsakis

[hylang]: https://docs.hylang.org/en/stable/
[leanpub]: https://leanpub.com/hy-lisp-python
[clojure]: https://clojure.org/
[racket]: https://racket-lang.org/
[flask]: https://github.com/pallets/flask
[docker]: https://www.docker.com/
[make]: https://opensource.com/article/18/8/what-how-makefile
[wsl2]: https://nickjanetakis.com/blog/a-linux-dev-environment-on-windows-with-wsl-2-docker-desktop-and-more
[fullstackpython]: https://www.fullstackpython.com/flask.html
[gh]: https://github.com/hylang/hy/issues/329
[hy-flask]: https://github.com/sophiabrandt/hy-flask