giddie

giddie

Ok_then - The Swiss Army Knife for tagged tuple pipelines

GitHub: GitHub - flexibility-org/ok_then: The Swiss Army Knife for tagged tuple pipelines
HexDocs: OK then... — ok_then v0.1.0

I just couldn’t quite find what I was looking for when it came to handling tagged tuples. In particular, I really wanted to find an elegant solution to the problem of missing return values that are not errors. Because sometimes, a missing return value is an error, and other times it’s not. Rust handles this very elegantly, using separate Result (Ok / Error) and Option (Some / None) types, often in tandem.

So I decided I had to write my own solution for handling tagged tuples. And I’m quite pleased with the result. Of course I have to give credit to those who have gone before, such as the authors of the “OK” and “Croma” packages.

The most controversial feature will be the addition of :none alongside :ok and :error, though this becomes mostly transparent in pipelines.

There are a few more functions I’d like to add, in particular to the Result.Enum module. And I’d also like a monadic “with”-like macro, similar to that offered by the “OK” package.

Most Liked

dimitarvp

dimitarvp

What does a new convention of :ok / :none achieve that hasn’t been achieved by the :ok / :error convention?

If a function does not return data then it can just return :ok (not a tuple).

When you say semantics and consistency, I am not seeing it. It’s OK to have a personal preference but I don’t see the value.

As for nil, well, I think we all know that’s a huge and very expensive mistake in computer science in general. But as others pointed out, nil is also an atom so replacing it with another one doesn’t help much. Not sure I am seeing the argument for the stack traces as well.

Maybe I just don’t get it though.

dorgan

dorgan

There are some functions that return none, like erlang:dist_ctrl_get_data, erlang:system_info, but it seems in those cases it means “set to none” or “not available” rather than “not found”

giddie

giddie

Yes, absolutely. The ok_then library will also accept any bare atom (:some, :success, :ok, …). It interprets these internally as {:ok, {}}, {:some, {}}, ..., so mapping functions will receive {} as the wrapped value, and that’s what unwrap!() would return too. It’s just that :none is returned by Result.from/1 and Result.map/2 if they receive nil as a value to wrap, and there are shorthand functions to handle :none, such as default/2 and none_then/2.

So if you want, you could fall back to a bare :ok quite easily like this:

Result.from(nil)      # :none
|> Result.default({}) # :ok

# or

Result.from(nil)         # :none
|> Result.none_then(:ok) # :ok

I’d be interested in some testcases, actually. Could I tempt you to come up with some examples of vanilla Elixir that we could compare to an equivalent using the ok_then library?

stevensonmt

stevensonmt

This addresses a real desire for me having gotten used to Option/Result types with Rust and Elm. The discussion in this thread has shown how much thought you put into the API design. Thanks for putting this out!

giddie

giddie

That could be interesting, yes. There certainly would need to be more buy-in for a pattern like this, but I think we could provide support with an additional Option module. The significant advantage I wanted to introduce with ok_then was making it hard to produce {:ok, nil}, and a solid mechanism for falling back to default values. For this purpose, I do think {:ok, value} | :none works just as well as {:some, value} | :none.

The existing Result module also has the benefit that if code later begins returning {:error, reason} values too, the downstream code won’t break, since Result.unwrap_or_else/2 will return the “else” for any tag that isn’t :ok - you might just want to add Result.error_consume/2 somewhere to log the error. In Rust, the type would change from Option<T> to Result<Option<T>>, and the compiler would provide help in adjusting downstream code. We don’t get that in Elixir.

But yeah, for people who want to reserve Result types for :ok/:error, and want a separate type for :some/:none, it should be quite easy to add additional support. The Result module itself probably could be used as-is, since in theory it’ll only be used to wrap Options, which should never be nil. I’m imagining something like this?

"hello"
|> Option.from()                    # {:some, "hello"}
|> Result.from()                    # {:ok, {:some, "hello"}}
|> Result.unwrap!()                 # {:some, "hello"}
|> Option.unwrap_or_else("default") # "hello"

And then I guess we’d want methods such as Option.default, Option.or_else etc…, pretty much identical to existing Result functions, but with :some as the happy tag instead of :ok.

Where Next?

Popular in Libraries Top

kip
Image is an image processing library for Elixir. It is based upon the fabulous vix library that provides a libvips wrapper for Elixir. I...
574 16576 179
New
kevinlang
Hey all, We have made an Ecto3 Adapter for SQLite3, ecto_sqlite3! We have successfully on-boarded the full suite of integration tests (...
New
sabiwara
Dune is a sandbox for Elixir and aims to safely evaluate user-provided code. You can try it out using this basic Elixir playground made ...
New
woylie
I released Doggo, a collection of unstyled Phoenix components. Features Unstyled Phoenix components. Storybook that can be added to...
New
achempion
Hi, I would like to tell about my initiative to further maintain and develop Waffle project which is the fork of Arc library. The progre...
New
danschultzer
In short Plug n’ play OAuth 2.0 provider library. Just set up a resource owner schema with Ecto (your user schema), install the dependen...
New
aditya7iyengar
Rummage.Ecto and Rummage.Phoenix provide ways to perform Searching, Sorting and Pagination over Ecto queries and Phoenix collections. Fo...
New
Qqwy
While not as prevalent as in imperative languages, arrays (collections with efficient random element access) are still very useful in Eli...
New
mischov
import Meeseeks.CSS html = HTTPoison.get!("https://news.ycombinator.com/").body for story &lt;- Meeseeks.all(html, css("tr.athing")) do...
New
New

Other popular topics Top

srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call th...
New
William
I would like to know that is there any online source for learning Phoenix Framework for building E-Commerce Store? Any advantage on build...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New

Sub Categories:

We're in Beta

About us Mission Statement