Uptight

Uptight is an infectious (as in IO monad) library answers two pain points of Elixir programming:

  1. Insufficiently tight types when it comes to distinguishing texts from binaries, as well as detachment of Base-encodings from binaries.
  2. Lack of an iron-gauntlet approach to errors and failure in Erlang and Elixir.

To address (1), we present the following types:

To address (2), we present the following types:

Offensive programming in Elixir

We often want to propagate an error to third parties, be it frontend programmers, end users or colleagues. It means that along with whatever context information BEAM generates for error handling, which often (but not always) is readable by Erlang/Elixir developers, we would like to also provide human-readable description of what went wrong, ideally together with some values.

In absence of blessed way to do so, I have reinvented assert came up with my own. It's a bit clumsy thus far, but all you need for it are Uptight.Result and Uptight.Trace. Here's the "pattern" for writing failable functions in such way that error getting propagated all the way the frontend is still handlable:

  defp poc_submission_output_to_raw_score(xkv, _opts) do
    Uptight.Result.new(fn ->

      # Demonstration that we can match against Ok, extracting the values
      %{"has `order` field set" => %Ok{ok: vertices}} = %{
        "has `order` field set" => Uptight.Result.new(fn -> xkv["order"] end)
      }
      %{"has `distance` field set" => %Ok{ok: distance}} = %{
        "has `distance` field set" => Uptight.Result.new(fn -> xkv["distance"] end)
      }

      # We can do all the matches we would normally be able to do in Elixir
      %{"solution vertices exist in the data set" => %Ok{ok: [{x0, y0} | ctail]}} = %{
        "solution vertices exist in the data set" =>
          Uptight.Result.new(fn -> path_to_coordinates(vertices, poc_data()) end)
      }
      {ref_distance, xf, yf} =
        Enum.reduce(ctail, {0.0, x0, y0}, fn {x1, y1}, {d, x0, y0} ->
          {d + distance({x0, y0}, {x1, y1}), x1, y1}
        end)
      ref_distance = ref_distance + distance({xf, yf}, {x0, y0})

      # Even exploit one of my favorite capabilities of Erlang/Elixir's pattern matching:
      # ** equality matching **! Note that rounded_distance and rounded_distance must be equal!
      %{"distance reported is correct" => {rounded_distance, rounded_distance}} = %{
        "distance reported is correct" =>
          {(1_000 * ref_distance) |> round(), (1000 * distance) |> round()}
      }

      # Note that at no point are we actually doing any validation. We just state our intent,
      # tagging matches with human-readable strings and let it crash, deferring the responsibility
      # of catching the failure to `new` function of Result and deferring the responsibility to
      # crash again to the user of our function. Paradoxically, having a somewhat defensive wrapper
      # allowed us to crash as loudly as we want while writing code. It's an amazing feeling.

      rounded_distance
    end)
  end

There's an issue in Doma's megalith repository aiming to fix error reporting ergonomics. According to Elixir macro gurus, it's possible to turn this pattern to a macro, because there are non-hermetic macros in Elixir!