Qqwy

Qqwy

TypeCheck Core Team

Solution: library to do pattern-matching with general ok/error types in case/with statements -- v1.0.1

logo_text

hex.pm version Build Status Documentation

Solution is a library to help you with working with ok/error-tuples in case and with-expressions by exposing special matching macros, as well as some extra helper functions.

hex - docs - github


Rationale

ok/error tuples, which are also known by many other names some common ones being ‘Tagged Status’ tuples, ‘OK tuples’, ‘Success Tuples’, ‘Result tuples’, ‘Elixir Maybes’.

Working with these types is however a bit complicated, since functions of different libraries (including different approaches in the Elixir standard library and the Erlang standard library) indicate a successful or failure result, in practice, in one of the following formats:

  • {:ok, val} when everything went well
  • {:error reason} when there was a failure.
  • :ok, when everything went well but there is no useful return value to share.
  • :error, when there was a failure bht there is no useful return value to share.
  • {:ok, val, extra} ends up being used by some libraries that want to return two things on success.
  • {:error, val, extra} ends up being used by some libraries that want to return two things on failure.
  • In general, {:ok, ...} or {:error, ...} with more elements have seen some (albeit luckily limited) use.

Clearly, a simple pattern match does not cover all of these cases. This is where Solution comes in:

  1. It defines clever guard macros that match either of these groups (is_ok(x), is_error(x), is_okerror(x))
  2. It defines macros to be used inside special case and with statements that use these guards and are also able to bind variables:

For instance, you might use ok() to match any ok-type datatype, and error() to match any error-type datatype.
But they will also bind variables for you: So you can use ok(x) to bind x = 42 regardless of whether {:ok, 42}, {:ok, 42, "foo"} or {:ok, 42, 3,1,4,1,5,9,2,6,5} was passed.

Examples

Guards

Solution exposes three guard-safe functions: is_ok(x), is_error(x) and is_okerror(x)

  • ok(x) will match :ok, {:ok, _}, {:ok, _, _}, {:ok, _, _, __} and any longer tuple whose first element is :ok.
  • error(x) will match :error, {:error, _}, {:error, _, _}, {:error, _, _, __} and any longer tuple whose first element is :error.
  • okerror(x) matches both of these.

Solution also exposes versions of these that take a ‘minimum-length’ as second argument. A length of 0 works jus the same as above versions. Longer lengths only match tuples that have at least that many elements (as well as starting with the appropriate tag).

SCase

Solution.scaseworks like a normal case-statement,
but will expand ok(), error() and okerror()macros to the left side of ->.

 scase {:ok, 10} do
  ok() -> "Yay!"
  _ -> "Failure"
  end
#=> "Yay!"

You can also pass arguments to ok(), error() or okerror() which will then be bound and available
to be used inside the case expression:

 scase {:ok, "foo", 42} do
   ok(res, extra) ->
      "result: \#{res}, extra: \#{extra}"
   _ -> 
      "Failure"
    end
#=> "result: foo, extra: 42"

Note that for ok() and error(), the first argument will match the first element after the :ok or :error tag.
On the other hand, for okerror(), the first argument will match the tag :ok or :error.

SWith

Solution.swith works like a normal with-statement,
but will expand ok(), error() and okerror() macros to the left side of <-.

 x = {:ok, 10}
 y = {:ok, 33, 44, %{a: "other stuff"}}
 swith ok(res)  <- x,
       ok(res2) <- y do
         "We have: \#{res} \#{res2}"
    else
      _ -> "Failure"
  end
#=> "We have: 10 33"

For more examples and more info about the helper functions, check the GitHub page or the Documentation. :slightly_smiling_face:


Please let me know what you think!

Most Liked

Qqwy

Qqwy

TypeCheck Core Team

Solution has had quite some time during which it seems like the interface as it currently stands works well.

Therefore, a stable 1.0 version will probably be released soon :slight_smile: .

Qqwy

Qqwy

TypeCheck Core Team

An excellent question. I most certainly did!

Solution is meant to be more general and more lightweight/idiomatic in its approach.
Let’s compare it with the six other commonly mentioned libraries that work with ok/error tuples:

Comparing it to the ok library:

  • OK requires ok/error tuples to always exactly have two elements (the first being :ok or :error, the second being a value). This means that plain :ok or :error are not handled, nor are results like {:ok, value, meta} which are relatively common in production Elixir (c.f. Ecto Multi).
  • OK introduces a new ‘keyword’ called OK.for which sort of takes the place of Elixir’s with but not completely (I think?), which is also subject to above caveat.
  • OK has a wrapper called OK.try which is sort of like OK.for but wraps it in an extra try/rescue block (I think?).
  • OK contains a couple of custom operators to allow ‘monadic piping’.

Solution’s swith and scase statements on the other hand work 1:1 like the built-in counterparts, except that you can add the ok(...) and error(...) macros at the LHS of the matches to match any ok/error tuple that has at least the required length. I believe that the library therefore has much less of a learning curve and much less mental overhead.

Comparing it to the result library:

  • Result has a bunch of monad-inspired functions, but no overloaded case or with statements.
  • Just like OK, Result only accepts exactly the format {:ok, value}/{:error, problem}.

Comparing it to the exceptional library:

  • Exceptional has features to declaw exceptions and turn them into plain structs or possibly ok/error tuples, and reraise them later. Handling exceptions is not a design goal of Solution, so it does not have this functionality.
  • Exceptional also has custom ‘monadic piping’ operators.
  • Handling different kinds of tagged tuples is not a design goal of Exceptional, so it does not have functionality to work with these datatypes (other than a basic ‘normalization’ conversion, which also does not handle ok/error tuples with more than one value inside). EDIT: It’s slightly more nuanced than that. see this follow-up.

Comparing it to the towel library:

  • Towel also takes the approach of a ‘monadic pipeline’. Not with special syntax this time, but using a couple of functions (that are probably part of your global namespace since it advocates to use Towel).
  • Towel does not have support for values of the type :ok/:error nor for {:ok, multiple, things}`.
  • Towel’s last commit was more than two years ago.

Comparing it to the ok_jose library:

  • OkJose overloads |> to work differently for ok/error tuples.
  • OkJose allows you to create other overloaded versions of |>.
  • OkJose does not have support for values of the type :ok/:error nor for {:ok, multiple, things}`.
  • OkJose’s last commit was more than two years ago, and the README mentions that it was made before with was available in Elixir.

So the Tl;Dr is that Solution has a slightly different approach to these libraries:

  • It strives to support all possible ok/error values that are used in practice: Not only the two-element tuple versions, but also the single atoms :ok and :error, as well as the versions with more than two elements like {:ok, data, metadata}.
  • It strives to be lightweight by using the existing case and with syntax, rather than adding variants of the pipe-operator and/or require an understanding of monads to be used successfully.
  • It is focused on working with ok/error tuples. It does not deal with exceptions or other kind of failure indications.

I hope that answers your question! :smile:

Qqwy

Qqwy

TypeCheck Core Team

As a heads-up: the issue has been resolved! :slight_smile:

Version 1.0.1 has been released.

Where Next?

Popular in Announcing Top

mathieuprog
Hello :waving_hand: Allow me to introduce you to Tz, an alternative time zone database support to Tzdata. Why another library? First a...
New
seancribbs
Today I released a new dialyzer Mix task as the dialyzex package! At the time we started writing this task, the existing dialyzer integra...
New
josevalim
Yes, yet another parser combinator library! Most of the parser combinators in the ecosystem are either compile-time, often using AST tra...
159 19417 141
New
Crowdhailer
The latest release of Ace (0.10.0) includes serving content over HTTP/2. I have started writing a webserver to teach my self more about...
New
woutdp
Hi! I wanted to introduce my latest project LiveSvelte. It allows you to render Svelte inside LiveView with end-to-end reactivity. It’s ...
New
cjen07
parameterized pipe in elixir: |n&gt; edit: negative index in |n&gt; and mixed usage with |&gt; are supported example: use ParamPipe ...
New
Hal9000
Here is my first stab at this. README pasted below. https://github.com/Hal9000/elixir_random Comments and critiques are welcome. Thank...
New
fuelen
Hey folks! Want to present a toolkit for writing command-line user interfaces. It provides a convenient interface for colorizing text...
New
markmark206
simple_feature_flags is a tiny package that lets you turn features on or off based on which environment (e.g. localhost, staging, product...
New
mplatts
With HEEX released we decided to start a components library using Tailwind CSS - check it out here: Petal Components. We also have a boi...
New

Other popular topics Top

TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 41989 114
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
Lily
In templates/appointment/index.html.eex: &lt;%= for appointment &lt;- @appointments do %&gt; &lt;tr&gt; &lt;td&gt;&lt;%= appoi...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement