Let’s continue our exploration of the wonders of local development with containers.

This time, we’ll delve into the world of Haskell programming. Instead of burdening your local environment with the entire toolchain, we’ll opt for a more streamlined approach: creating a dev container. This example will illustrate how effortlessly you can integrate new tools and programming languages without causing any disruptions to your local system.

What is Haskell#

Haskell, a pure functional programming language, though not as commonly used in the industry, is a powerful language whose fundamentals can significantly enhance your programming skills.

However, Haskell comes with a complex toolchain and numerous dependencies, potentially posing challenges for some systems. If you’re not a professional Haskell developer, the process of installing all these tools may not be worthwhile.

This is where dev containers come to the rescue, allowing you to sidestep these issues while still enabling you to experiment with Haskell. Let’s get started.

Personally, I find Haskell to be a fascinating language that offers valuable insights into novel techniques and encourages a different perspective on problem-solving. Experimenting with Haskell has been particularly enjoyable for me, providing a unique opportunity to broaden my understanding and approach to coding challenges.

Creating the Haskell dev container#

As usual, we start by creating our local directory with a child directory called .devcontainer, inside this dir let’s create a new file called devcontainer.json, then add this code.

{
    "name": "Haskell Base",
    "image": "mcr.microsoft.com/devcontainers/base:ubuntu",
    "customizations": {
        "vscode": {
            "extensions": [
                "haskell.haskell",
                "visualstudioexptteam.vscodeintellicode",
                "visualstudioexptteam.intellicode-api-usage-examples",
                "streetsidesoftware.code-spell-checker"
            ]
        }
    },
    "postCreateCommand": "zsh .devcontainer/installation.sh",
    "runArgs": ["--name", "haskell_devcontainer"]
}

Let’s break down its key elements:

The name field is set to “Haskell Base,” providing a recognizable identifier for this development environment.

The image field points to the Docker image “mcr.microsoft.com/devcontainers/base:ubuntu.” This image serves as the foundation for the development environment.

Within the customizations section, various Visual Studio Code extensions are specified. These extensions enhance the Haskell development experience.

The postCreateCommand field designates a post-creation command that runs after the dev container is set up.

Finally, the runArgs field supplies extra arguments for the docker run command when initiating the dev container. It assigns the name “haskell_devcontainer” to the container for easy identification.

As you may have noticed, we need to create a script to install Haskell in our container. To craft this file, we follow the instructions provided by the Haskell page. This shows us how we can easily configure our container as if it were a standard local installation. This shows the power and simplicity of working with local containers.

Within the .devcontainer directory, create a new file named configuration.sh and insert the following code into this newly created file.

#!/bin/zsh

sudo apt update
sudo apt install -y build-essential curl libffi-dev libffi8ubuntu1 libgmp-dev libgmp10 libncurses-dev libncurses5 libtinfo5

curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh

echo 'export PATH="$PATH:$HOME/.ghcup/bin"' >> ~/.zshrc
source ~/.zshrc

echo 'export PATH="$PATH:$HOME/.ghcup/bin"' >> ~/.bashrc
source ~/.bashrc

echo "IMPORTANT: If bash doesn't find cmds, run: source ~/.bashrc"

In this script, we’re simply adhering to the installation recommendations provided by the official Haskell documentation for Linux. no extra steps or magic is needed for the container; we can treat it just like a local installation.

After preparing this, open your project in a dev container, go through the setup process (simply respond with ‘Y’ to all questions), and you’re all set to delve into Haskell on your machine.

Testing Haskell#

Let’s create a simple program to test our envirment. Create a new file called “main.hs” and add the following code.

main = do
    putStrLn "What is 2 + 2?"
    x <- readLn
    if x == 4
        then putStrLn "You're right!"
        else putStrLn "You're wrong"

Now let’s run it by importing it to the interpreter

ghci main.hs

The ghci REPEL will open, now let’s run our function by just calling its name inside ghci

main

This will ask you for an input and will print the result

What is 2 + 2?
4
You're right!

Congratulation, your Haskell environment is working correctly!

Conclusion#

In this exploration of local development with containers, we’ve ventured into Haskell programming, sidestepping the complexities of the language’s toolchain by creating a streamlined dev container. Despite Haskell’s relative industry underuse, its power for skill enhancement remains evident.

Dev containers prove invaluable, allowing us to experiment freely with Haskell without the hassle of extensive toolchain installations. Crafting a Haskell dev container is straightforward, following the official documentation to seamlessly configure it as a standard local installation.

Our simple program confirms that our setup is working correctly. With this, your Haskell environment is ready for exploration. Happy coding!