Eta in Practice: Working With Haskell Packages

Eta works well with Haskell and Java

You’re really excited about Eta and you want to start building cool apps with it. But to build stuff without re-inventing the wheel, you’re going to want to have libraries for all the basic tasks like connecting to REST APIs, parsing JSON, and so on.

Though Eta is new, there are no shortage of libraries that you can start using
right away. One reason is because it has a smooth Java Foreign Function Interface which lets you interoperate with Java methods pretty easily. We’ve already had developers build applications interfacing with the Java SDKs for Apache Kafka, Apache Spark, Amazon Kinesis Streams, and even JDBC for uniform access to SQL databases.

Check out the on-going series on that for more information.

The other reason we don’t have a shortage of libraries is that we’ve stayed so close to Haskell syntax that the vast majority of packages on Hackage (Haskell’s package repository) compile out-of-the-box!

How we go about this will be the focus of this post.

How Does It Work?

The Hackage ecosystem works like this: a large number of packages use pure Haskell and a small subset will resort to using the C Foreign Function Interface (FFI) to be able to call C functions from Haskell. The interesting part is that the packages that use C FFI cluster near the root of the dependency graph for any practical project one works on.

In order for Eta to access this, we needed a way to deal with the C FFI calls. One way to do it would be to use the Java Native Interface to work with C, but this would make the Eta toolchain complex and platform-dependent. To stay true to Java’s selling point of platform independence, we took another route: the eta-hackage patches repository.

Etlas, the build tool for Eta, will automatically sync with this repository. Every time it notices that you’re attempting to install a package for which there exists a patch in eta-hackage repository, it will apply the patch before building the package from source. These patches can vary from being minor to rather involved and often replace the C FFI declarations and implementations with equivalent Java declarations and implementations.

For Example

Suppose you try to install the binary package, which is a serialization solution in the Haskell ecosystem. Here is a sample output from Etlas:

> etlas install binary
Resolving dependencies…
Found patch in eta-hackage for bytestring-0.10.8.2
Found patch in eta-hackage for containers-0.5.10.2
Configuring containers-0.5.10.2…
Configuring bytestring-0.10.8.2…
Building containers-0.5.10.2…
Building bytestring-0.10.8.2…
Installed bytestring-0.10.8.2
Installed containers-0.5.10.2
Configuring binary-0.8.5.1…
Building binary-0.8.5.1…
Installed binary-0.8.5.1

As seen above, Etlas will notify you when it applies a patch.

What if the patch doesn’t exist?

Suppose the patch doesn’t exist for a package that you want to install and it fails as a result. You can submit a request or contribute a patch to eta-hackage. Once the patch has been merged into the masterbranch of eta-hackage, you just have to run etlas update to update the local patch set. You can then proceed to try the package installation again and it should work.


Now we know we can integrate with Hackage pretty nicely. We’ll end with a practical tutorial on doing data transformations in Eta.

Example: Transforming JSON

Imagine you had a bug where you were measuring clicks and storing them as JSON and your math was wrong and you were off by 50 and your boss was yelling at you to get it fixed as soon as possible to get more accurate metrics. Well since you have Eta which makes data transformations simple, you roll up your sleeves and get going.

The way to do it in Haskell is to use the famous lens library, along with the lens-aeson library which provides specific lenses for working with JSON. Lenses are the power tool for making highly targeted updates to structured data in a strikingly succinct manner.

We’ll also prettify the JSON at the end to make the ouput a bit cleaner.

Follow along to get the Etlas project up and running!

1. Install Eta if you haven’t already.

2. Clone the eta-examples repository.

> git clone https://github.com/typelead/eta-examples

3. Browse to the 5-lens folder.

> cd 5-lens

3. Install all the dependent packages.

You can see the packages that are required by looking at the build-depends field of the lens-ex.cabalfile:

build-depends: base >=4.8 && <4.9
, aeson
, aeson-pretty
, bytestring
, lens-aeson
, lens

To install the dependencies automatically, run


> etlas install --dependencies-only

4. Run the code.

The code is shown below:

Line 1: This declares the module.

Line 3–7: Imports the relevant modules that contain the functions that we’re going to use.

Line 9: The main function is the entry point for an Eta program. The function has type IO () which means it performs input/output and will eventually run () (pronounced as “unit”) which is equivalent to returning void or nothing in other languages.

As you can see, there’s a clear separation of code that performs real work vs code that just computes something. This will make things tremedously simpler when you start getting into concurrency and makes it more modular and testable as well!

Line 10: We start a do block with the dokeyword that is indentation-based, which means that we must put all the sub expressions inside the block at the same indentation level, beyond the current one, as shown above.

This is where we can perform I/O.

Line 11: The let keyword allows you to define bindings or immutable values. In this particular line, we define the jsonbinding that’s a simple string. This is also indentation-based you can define multiple bindings at the same indentation level.

Line 12: We define the json'binding which stores the updated json that fixes the mistake. It uses the concept of lenses. Lenses allow you to zoom into the exact portion of the data structure you want in a type-safe way that composes. This means that you can combine multiple lenses zooming in more and more after each combination using the . operator which just so happens to be plain old function composition!

In a language with a powerful type system and global type inference, you would hardly know that you’re even programming in a statically typed language!

The values . members . _Integral is the composite lens which zooms into each of the array elements, zooms into the values of the objects, and tries to see if the value is an integer or not. The %~ operator means that the lens provided to the left should be updated with the function provided on the right (add 50).

Line 14: We use the case expression to evaluate the expression between the case and the of— in this case (no pun intended) we’re doing to decode the string into a JSON. And if you haven’t guessed yet, this is also indentation-based.

Line 15: If the decoding worked (which it obviously will in this case), this branch will be taken and it’ll perform I/O by printing out the prettified JSON.

Note: The $ simply means to apply the function on the left to the argument on the right, so a $ b $ c = a (b (c)) — it’s a trick to avoid lots of parenthesis.

Line 16: If the decoding failed, this branch will just print out “Bad JSON!”

Ok, now that we have a basic understanding of what’s going on, let’s run this and see what happens.

> etlas run
[
{
"clicks": 51
},
{
"clicks": 52
},
{
"clicks": 53
}
]

Great! That’s some pretty JSON.


If you want to figure out what libraries/packages you can use for a particular task, run a Google search with your task description with “haskell” at the end (e.g. “json parsing haskell” will point you to the aeson package) and you’ll get packages and tutorials that you can instantly apply to writing Eta programs as well.

To see the list of packages that have been tested and are known to work with Eta right now, check this list out.

If you face any issues, you can reach out to the Eta community at:

Gitter / IRC / Slack / Google Group / GitHub / Twitter

Thanks for reading and stay tuned for more tutorials and updates!