Elchemy — Write type-safe Elixir code with Elm’s syntax — part 1 — Introduction
Few words of introduction
We all love Elixir. It’s expressive, it’s extremely readable and - first and foremost - It runs on BEAM — an incredible virtual machine that turns concurrency into something as natural and expected as NullPointerExceptions in Java 7.
But as every language does, it also has its flaws. Some are inherent, some are just religious choices that had to be made one way or another; and as the user (or rather fan) base grows, so does the demand to express your own preferences. Changes like piping to the last argument instead of the first can be introduced with simple macros. Some people craving for more advanced ideologies create entire toolsets extending the language (like Railway Oriented Programming or Saša Jurić’s Exactor). Some others commit to entire frameworks that start to look like DSLs instead (Witchcraft).
But even smartest macro can’t solve everything without surpassing the biggest meta-programmer’s limitation: syntax. So that’s what we changed.
Arguably one of the loudest religion choices is typing. Private jokes like “Strong typing is for weak minds” and “If you shoot yourself in the foot, it’s easier to solve the problem by being careful not to aim the gun at your foot than it is to make guns that don’t point down” roam around the community. Whether types are hot or not, is out of this article’s scope.
I like strong types. And I like Elixir.
And that’s why Elchemy was made.
What does strong typing give us anyway?
Generally strong typing pays attention so you don’t have to.
You put a typo in function params?
Compiler will tell
Your arguments are in wrong order.
Compiler will tell
You wrongly treat a list of {:ok, value}
tuples as if they were just value
’s.
Compiler will tell.
So whether your application works on complex nested structures, you like extra safety, or you just suffer from short attention span, static typing probably will serve you well.
Elchemy to the rescue!
With all of those values in mind, we came out with Elchemy:
Without further and unnecessary descriptions. Elchemy is entirely about turning this:
Into this:
Does it work? Check by yourself.
“But wait. I just typed
a : String
a = 1And it totally went through. Where is the entire type safety in that? You suck!”
Worry not. The type safety’s there. Just not in the browser, but living safely in the depths of the entire toolkit to integrate Elchemy with your existing Elixir project.
And to prove and learn the basics of it, below we’ll write a simple example program from scratch.
We don’t have any project so we’ll start a new one:
$ mix new article_example_elchemy
$ cd article_example_elchemy
Now we also need to install Elchemy and initialize it in our project
So let’s do that and try out how our type safety is doing.
$ npm install -g elchemy
$ elchemy init
Terminal responds to us with:
Elchemy initialised. Make sure to add:compilers: [:elchemy, :yecc, :leex, :erlang, :elixir, :app],
elixirc_paths: ["lib", "elm-deps"],
elchemy_path: "elm",to your mix.exs file
Then run mix test to check if everything went fine
So I open an editor of my choice
$ atom mix.exs
And add the missing lines
Great! We’re good to go. Let’s type mix test
to make sure that we’re all set.
We should see a ton of logs and warnings. But don’t worry — as long as the tests are passing everything’s fine!
Check. So now let’s open elm/Hello.elm
and implement our first example.
*If you don’t have any Elm language plugin/extension/layer in your editor, you should probably install one now*
We’ll implement a functional programming ‘hello world’ — a sum of a list!
Since most of the editors organize files based on git repository files - as a minimum setup let’s first make an initial commit.
git init && git add . && git commit -am "init"
And let’s go!
Ok. So as we are grown up developers, we’re going to start with a unit test suite for our future code.
Let’s write a simple test case making sure that our sum of integers is what it’s meant to be.
*If you’re not yet familiar with ExUnit tests syntax you should probably be reading this article instead*
In your project, open test/elchemy_test.exs
and paste this code
Run mix test
once again and we should see this result:
Great!
Head back to elm/Hello.elm
and let’s implement the sum function.
We’re gonna start with a type signature. Our sum
function obviously takes a list and returns a sum. Presumably an integer. So let’s type it!
Ok. Now it’s the time for our implementation.
Classically we’ll write a sum as a recursion, where we take a head of a list and add it to the application of the same function on the tail , with an exception of returning just 0
if the list is empty.
Which should look like this
Save our file and…. wait a second. There’s an error.
Oh… That’s right! Since we’re no longer in a world of dynamic typing, we can’t just add any two things together. Addition is an operator reserved for numbers only.
That means our type signature is incorrect.
Let’s fix it by changing to sum : List Int -> Int
Perfect!
Let’s run our test case again and see how it’s doing
Alright! We’ve got our first functional program. Now let’s add some bells and whistles to it. We’ll start by removing the test we just wrote. We don’t need an entire test case just for a simple sum function. Let’s make it a doctest!
Remove the test case and write a beautiful doc for our newly born function.
Same as in Elm we write a docstring using {-| -}
comment block. So let’s do exactly that
That should be just right. Let’s run our mix test
again.
If we removed our previous test case, we should now have 7 tests 0 failures
report from our test suite; each one of our doctest lines being a separate test case.
But what did we write anyway?
Although the Elchemy code is sufficient on it’s own and there is no need to look at the generated output, it’s good to make a sanity check, so we know what does the written code do to the rest of our codebase.
To do that, we can look at generated Main.ex
file inside our output lib/
folder.
This is the part relevant to what we just wrote.
Let’s break it down.
At #23 We start with a @doc
string that works as a documentation for our function.
Then between #26–36 we have three generated examples in Elixir doctest format.
At Line #41 We can see the typespec signature for a curried version of our function — this is how our function will be called from inside Elchemy.
Line #42 Is a regular typespec, generated only for function usage out of Elchemy — directly from Elixir code
Line #43 is a bit of Elchemy magic that makes our function accessible in curried form
Line #44–49 is exactly what we’d do writing the function by hand, with a tiny exception of a mysterious dot in sum.(rest)
which for now will remain unexplained. Generally it’s because in Elchemy everything is curried.
That’s all for part one.
In the next part we’ll learn how to create an entire library, using
modules from elchemy-core, defining your own types, type aliases and operators.
| Update Name was changed from Elmchemy to Elchemy
GO TO PART TWO
Hacker Noon is how hackers start their afternoons. We’re a part of the @AMI family. We are now accepting submissions and happy to discuss advertising & sponsorship opportunities.
If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!