Functional Programming in Elixir with Witchcraft 
Tags: elixir
Published: 2022-02-15 13:06:59

While Elixir is a functional programming language, it is different from most of the other popular functional languages like Haskell, Scala, OCaml, and F#.

Elixir pragmatically handles concurrent systems with high fault tolerance. In other words, Elixir is an FP language because this naturally fits it, and not for its own sake. So, porting idioms blindly from Haskell to Elixir can lead to undesired results.

At the same time, Elixir users should more frequently visit the whole wonderful universe of functors, monads, and other curiosities. Good libraries have been introduced for operating with algebraic structures.

In this article, I want to introduce you to a library called Witchcraft and show how you can use it to emulate Haskell-style programming in Elixir.

## What is Witchcraft?

[Witchcraft](https://witchcrafters.github.io/) is a library written by [Brooklyn Zelenka](https://twitter.com/expede?lang=en) that provides Elixir programmers with predefined abstractions for writing Haskell-like code. Witchcraft takes into account the eccentricities of Elixir, such as the frequent use of the pipe operator.

In other words, Witchcraft is a library for writing Haskell 'fan fiction' in Elixir.

It connects with a few other libraries. All in all, the whole Witchcraft complex consists of four parts:

* **[Quark](https://github.com/witchcrafters/quark)** provides some functional building blocks, including flipping arguments of a function in place, a macro for currying functions, and other commonly used things.
* **[TypeClass](https://github.com/witchcrafters/type_class)** provides a good interface for generating type classes and their instances.
* **[Witchcraft](https://github.com/witchcrafters/witchcraft)** implements common algebraic and categorical abstractions, such as monads, functors, etc.
* **[Algae](https://github.com/witchcrafters/algae)** implements a domain-specific language (DSL) for creating composite data types like you would in Haskell, and also contains commonly-used composite data types.

Now, let’s try working with the library.

## String Validation and Processing with Witchcraft

We will go through a credit card validation exercise.

Given a string that should contain a credit card number, we need to make sure that:

* it contains 16 digits
* it has at least two different digits
* the final digit is even
* the sum of the digits is more than 16

We will do this exercise with `Either`, one of the predefined data types that the Witchcraft complex offers.

### Set Up Witchcraft

First, add the necessary libraries to your `mix.exs` file and run `mix deps.get`.

```elixir
  defp deps do
    [
      {:witchcraft, "~> 1.0"},
      {:algae, "~> 1.2"},
    ]
  end
```

### Parsing the String

First, make a new module and import Witchcraft at the top of the module.

```elixir
defmodule Card do
  use Witchcraft

end
```

Then, write a function to parse the string into a list of digits and remove all non-digit items.

```elixir
def get_digits(cardnumber) do
  cardnumber
  |> String.split("", trim: true)
  |> Enum.map(fn x -> Integer.parse(x) end)
  |> Enum.filter(fn x -> x != :error end)
  |> Enum.map(fn {int, _rest} -> int end
end
```

In the code above, we split the card number into characters and map `Integer.parse` (safe integer conversion) over them. `Integer.parse` returns either `{int, rest_of_binary}` or `:error`. We filter out the errors and unpack the tuples with the last two functions.

```bash
iex(1)> Card.get_digits("3456-3233-5689-4445")
[3, 4, 5, 6, 3, 2, 3, 3, 5, 6, 8, 9, 4, 4, 4, 5]
```

After that, we can write our first validation function with `Either`.

### Quick Intro to the `Either` Data Type

`Either` is a type that contains one of two different options: `Left` or `Right`. Each of these wraps another type. `Left` stores failures, and `Right` stores successes.

```haskell
-- Either data type as defined in Haskell
data Either a b = Left a | Right b
```

Usually, we return it from computations that can fail when you want to know what kind of error made them fail.

When working with Witchcraft, you can use the predefined [Algae.Either](https://github.com/witchcrafters/algae/blob/main/lib/algae/either.ex) to create new instances of the `Either` data type. We can do that by using one of two structs: `%Algae.Either.Left{left: value}` or `%Algae.Either.Right{right: value}`.

Most likely, you've already run into it somewhere. In other languages, it’s sometimes called `Result` (in Rust, for example). It’s also structurally identical to the result tuple frequently used in Elixir.

```elixir
{:ok, result} -> {:right, result} -> %Algae.Either.Right{right: result}
{:error, reason} -> {:left, reason} -> %Algae.Either.Left{left: reason}
```

In fact, we could technically use the result tuple for our exercise. I've constructed it this way on purpose, so you can explore the machinery of Witchcraft while staying on more or less stable ground.

### Example of `Either`

Let’s look at an example of `Either`.

We have parsed a list of digits from the given string, and now we want to know whether there are 16 items in the list. If we took the easy way out, we could write something like this:

```elixir
def sixteen_digits(cardnumber), do: Enum.count(cardnumber) == 16
```

Unfortunately, the function above is not very composable: the return value loses the card number, so we won’t be able to pipe the result to do further computations on it.

Therefore, we want the result to be a more complicated structure such as `Either`.

* If there are 16 digits in the list, we want to return an `Either.Right` with the list.
* If there is a different number of digits, we want to return an `Either.Left` with `:not_16_digits`.

And here’s the function to do that:

```elixir
def sixteen_digits(cardnumber) do
  case Enum.count(cardnumber) do
    16 -> %Algae.Either.Right{right: cardnumber}
    _ -> %Algae.Either.Left{left: :not_16_digits}
  end
end
```

Let’s try it out in `iex`:

```bash
iex(1)> right_number = Card.get_digits("3456-3233---5689-4445")
[3, 4, 5, 6, 3, 2, 3, 3, 5, 6, 8, 9, 4, 4, 4, 5]
iex(2)> Card.sixteen_digits(right_number)
%Algae.Either.Right{right: [3, 4, 5, 6, 3, 2, 3, 3, 5, 6, 8, 9, 4, 4, 4, 5]}
iex(3)> wrong_number = Card.get_digits("444")
[4, 4, 4]
iex(4)> Card.sixteen_digits(wrong_number)
%Algae.Either.Left{left: :not_16_digits}
```

### Other Validation Functions

In the same way, we can make a validation rule for each requirement.

I suggest you try doing it yourself first since they will be similar to the first example.

To remind you, here are the conditions we need to check:

* The number has at least two different digits.
* The final digit of the number is even.
* The sum of the digits is more than 16.

Each of the conditions should have its own function. Each of the functions should take a list of numbers and return:

* `%Algae.Either.Right{}` with the list inside if the condition is fulfilled
* `%Algae.Either.Left{}` with the reason inside if the condition is not fulfilled

If you are ready to proceed, here are the functions that I will use further on in this article:

```elixir
def two_unique_digits(cardnumber) do
  unique =
    cardnumber
    |> Enum.uniq()
    |> Enum.count()

  case unique >= 2 do
    true -> %Algae.Either.Right{right: cardnumber}
    _ -> %Algae.Either.Left{left: :not_2_unique_digits}
  end
end

def final_even(cardnumber) do
  last_digit = Enum.at(cardnumber, -1)

  case rem(last_digit, 2) do
    0 -> %Algae.Either.Right{right: cardnumber}
    _ -> %Algae.Either.Left{left: :last_not_even}
  end
end

def sum_greater_than_16(cardnumber) do
  case Enum.sum(cardnumber) >= 16 do
    true -> %Algae.Either.Right{right: cardnumber}
    _ -> %Algae.Either.Left{left: :sum_smallr_than_16}
  end
end
```

### Connecting the Functions

But how can we join these functions together? Each of them takes a list but returns a struct. We can’t pipe them into each other because their types don’t match up.

To chain them, we need something that knows how to unwrap `Algae.Either` and apply other functions to what's inside, and Witchcraft has just the thing we need.

```elixir
definst Witchcraft.Chain, for: Algae.Either.Left do
  def chain(left, _), do: left
end

definst Witchcraft.Chain, for: Algae.Either.Right do
  def chain(%Right{right: data}, link), do: link.(data)
end
```

*From [Algae.Either](https://github.com/witchcrafters/algae/blob/main/lib/algae/either.ex).*

[`chain`](https://hexdocs.pm/witchcraft/Witchcraft.Chain.html#chain/1), which can also be written in Witchcraft as `>>>` (or `bind`), is the famous bind operator from Haskell: `>>=`. Let’s understand what it does.

As defined for `Either`, it will take a value — either `Left ` or `Right` — and a function from a regular value to `Either`.

In the case of `Left`, it will ignore the function and pass the value further. In the case of `Right`, it will apply the function to the value inside `Right`.

In our code, we can conveniently use `>>>` so that it looks pipe-y.

```elixir
def parse_number(cardnumber) do
  cardnumber
  |> get_digits()
  |> sixteen_digits()
    >>> fn x -> two_unique_digits(x) end
    >>> fn x -> final_even(x) end
    >>> fn x -> sum_greater_than_16(x) end
end
```

Unfortunately, the capture syntax (`&two_unique_digits/1`) doesn’t work here. (The macro will expand the code to nested captures, which Elixir doesn’t allow you to do via `&`.)

Combining the `Either` data type and its defined chain function lets us build up a chain of functions. Even though they take a list and return an `Either`, the functions can be chained to arrive at a result. The whole chain will return the list in case of success and the first encountered error in the case of failure.

We can try it out in `iex`.

```bash
iex(1)> Card.parse_number("4444-444-222-1")
%Algae.Either.Left{left: :not_16_digits}
iex(2)> Card.parse_number("4444-4444-4444-4444")
%Algae.Either.Left{left: :not_2_unique_digits}
iex(3)> Card.parse_number("4444-4444-4444-4435")
%Algae.Either.Left{left: :last_not_even}
iex(4)> Card.parse_number("1020-0000-0000-0000")
%Algae.Either.Left{left: :sum_smaller_than_16}
iex(5)> Card.parse_number("4545-3232-5423-6788")
%Algae.Either.Right{right: [4, 5, 4, 5, 3, 2, 3, 2, 5, 4, 2, 3, 6, 7, 8, 8]}
```

From this point onward, you can claim to have written monadic code in Elixir.

### Tinkering With the Codebase

Here are some minor changes we can make to the code so it looks nicer. These are not essential to your understanding but can give you more practice with the code example in question.

First off, Witchcraft provides a very handy `~>` operator that does the same as piping into `Enum.map`.

We can use it in our `get_digits` function.

```elixir
def get_digits(cardnumber) do
  cardnumber
  |> String.split("", trim: true)
  ~> fn x -> Integer.parse(x) end
  |> Enum.filter(fn x -> x != :error end)
  ~> fn {int, _rest} -> int end
end
```

After that, empty lists will cause some run-time errors with the `Enum.at` that we used, and having empty inputs probably isn’t part of the plan, so we can reject empty strings right at the start.

``` elixir
def get_digits(cardnumber) do
  digits =
    cardnumber
    |> String.split("", trim: true)
    ~> fn x -> Integer.parse(x) end
    |> Enum.filter(fn x -> x != :error end)
    ~> fn {int, _rest} -> int end

  case digits do
    [] -> %Algae.Either.Left{left: :empty_input}
    _ -> %Algae.Either.Right{right: digits}
  end
end
```

And finally, there is a macro that you can use in `parse_number` to simulate a more Haskell-like way of writing a sequence of actions in a certain context. This style is frequently called the [do-notation](https://hexdocs.pm/witchcraft/Witchcraft.Monad.html#monad/2).

```elixir
def parse_number(cardnumber) do

  monad %Algae.Either.Right{} do
    digits <- get_digits(cardnumber)
    a <- sixteen_digits(digits)
    b <- two_unique_digits(a)
    c <- final_even(b)
    d <- sum_greater_than_16(c)
    return(d)
  end
end
```

In the code sample above, we provide the kind of container that we will operate with — `%Algae.Either.Right{}`. Then we can do actions inside it, and the [`monad`](https://hexdocs.pm/witchcraft/Witchcraft.Monad.html#monad/2) macro will automatically chain our functions. In the end, it will return whatever we put in the return statement (but wrapped in the container).

It's very powerful, but also very confusing at first. If it is hard to understand what’s happening here, I encourage you to try out `monad []` first since it is very similar to [list comprehensions](https://elixir-lang.org/getting-started/comprehensions.html). In fact, if you think of this macro as generalized list comprehensions, you won't err too much.


### `Either` and the Result Tuple in Elixir

`Either` is very similar to the result tuple in Elixir, and I’ve done that on purpose to keep you on somewhat familiar ground.

In fact, you can implement the same thing without using Witchcraft.

```elixir
defmodule Card2 do

  def get_digits(cardnumber) do
    digits =
      cardnumber
      |> String.split("", trim: true)
      |> Enum.map(fn x -> Integer.parse(x) end)
      |> Enum.filter(fn x -> x != :error end)
      |> Enum.map(fn {int, _rest} -> int end)
  end

  def sixteen_digits(cardnumber) do
    case Enum.count(cardnumber) do
      16 -> {:ok, cardnumber}
      _ -> {:error, :not_16_digits}
    end
  end

  def two_unique_digits(cardnumber) do
    unique =
      cardnumber
      |> Enum.uniq()
      |> Enum.count()

    case unique >= 2 do
      true -> {:ok, cardnumber}
      _ -> {:error, :not_2_unique_digits}
    end
  end

  def final_even(cardnumber) do
    last_digit = Enum.at(cardnumber, -1)

    case rem(last_digit, 2) do
      0 -> {:ok, cardnumber}
      _ -> {:error, :last_not_even}
    end
  end

  def sum_greater_than_16(cardnumber) do
    case Enum.sum(cardnumber) >= 16 do
      true -> {:ok, cardnumber}
      _ -> {:error, :sum_smaller_than_16}
    end
  end

  def parse_number(cardnumber) do
    digits = get_digits(cardnumber)

    sixteen_digits(digits)
    |> chain(&two_unique_digits/1)
    |> chain(&final_even/1)
    |> chain(&sum_greater_than_16/1)
  end

  defp chain({:ok, result}, f), do: f.(result)
  defp chain({:error, _error} = result, _f), do: result
end
```

Here, we could also have used a [`with` statement](https://www.openmymind.net/Elixirs-With-Statement/), which handles tagged result tuples in a monadic way.

```elixir
def parse_number_with(cardnumber) do
    with digits <- get_digits(cardnumber),
         {:ok, a} <- sixteen_digits(digits),
         {:ok, b} <- two_unique_digits(a),
         {:ok, c} <- final_even(b),
         {:ok, d} <- sum_greater_than_16(c)
  do
    {:ok, d}
  else
      err -> err
  end
end
```

But the benefit of Witchcraft is that you get a specific data type with a specific chain function and a large set of types, type classes, and functions, all of which work well with each other to support a particular programming style.

## Why Use Witchcraft to Write Haskell-style Elixir?

There are quite a few benefits to using Witchcraft when writing Elixir:

- **Powerful abstractions** - for example, we have seen the `monad` macro, which generalizes over the `for` and `with` statements in Elixir. While `for` and `with` statements are monadic, they are basically made to handle just a few types (lists or tuples) in a certain way. This does cover a large part of your everyday needs, but there are other wonderful things you could create by applying the same pattern differently _once you see it_. For example, [Haskell's wiki lists nine standard monads](https://wiki.haskell.org/All_About_Monads#A_Catalog_of_Standard_Monads).
- **Pre-set patterns of code shared between typed functional programming languages.** If you know these patterns, you can basically go to any typed FP language, check the syntax for them, and continue working with essentially no productivity loss. It's the opposite of macros: it works the same way everywhere :)
- **More elegant code** - for example, a monadic way of handling tuples lets us escape from writing many ugly nested case statements. While Elixir's `with` statement already solves this particular problem, there are plenty of other places to apply these tools.

Unfortunately, Elixir isn’t really built for writing code like this. Therefore, using Witchcraft can also result in:

* A certain amount of unnecessary ceremony
* Performance penalties
* Angry stares from people that read your code

In general, the most significant benefit of trying out this programming style in Elixir is to train your programmer brain. Even though your code might not use Witchcraft or exactly follow these patterns as you write Elixir, it is good to know what exists out there.

## Summing Up and Further Witchcraft Resources

In this post, we looked at how you can write Haskell 'fan fiction' in Elixir using Witchcraft with a credit card validation code example.

Hopefully, you've seen that Witchcraft contains a ton of stuff, enough to satisfy your curiosity about this kind of programming and more.

But, while it is a good tool for teaching FP concepts, there aren't a lot of tutorials available. Brooklyn Zelenka did a [talk about Witchcraft](https://www.youtube.com/watch?v=psdG5iV57q0), but there isn't much else out there. If you would like to learn more about this programming style _right now_, your best bet is to start exploring a language like Haskell, F#, or OCaml (the latter two also have pipes).

Alternatively, you can explore typed BEAM by trying languages like [Gleam](https://gleam.run/) or [Hamler](https://www.hamler-lang.org/).

Happy coding!

**P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, [subscribe to our Elixir Alchemy newsletter and never miss a single post](/elixir-alchemy)!**