victorolinasc

victorolinasc

Should `Base.decodeXX` functions return more than just `:error`?

I was about to create an issue on the Elixir repo about the current behaviour of Base.decodeXX functions in core. I’ll first post it here and if people agree I’ll open the issue and send a PR. Just wanted to avoid polluting the issue tracker and getting some feedback beforehand.

Issue description

Base.decodeXX functions return a very unuseful :error only atom. This is fine for all the cases that you trust the encoding. Sometimes, though, we need to define some external “protocol” that will send/receive encoded binaries. In these cases, it is common to have something like:

def some_input_handling_fun(binary) do
  with {:ok, decoded} <- Base.decode64(binary),
         {:ok, result} <- do_something_with_decoded(decoded) do
      {:ok, result}
  end
end

The issue is that the return of decodeXX funs, if it fails, is simply :error which makes debugging this a bit tricky. If anything else returns :error (which it shouldn’t but you know how mankind works, right?) then we will need more context to understand what went wrong.

If this is ok, I can send a PR changing these specs and implementation here, here and here.

This is public API and so, I am not sure if this can be done as it would be backwards incompatible. I think this is a bug since many times it was mentioned that bad error “practices” in the core language should be treated as such. If this is not the case, then I’ll wrap the function locally anyway :slight_smile:

Environment

  • Elixir & Erlang/OTP versions (elixir --version):
elixir --version
Erlang/OTP 21 [erts-10.0.6] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]
Elixir 1.7.3 (compiled with Erlang/OTP 21)
  • Operating system:
uname -a

Linux 4.17.19-200.fc28.x86_64 #1 SMP Fri Aug 24 15:47:41 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux

Current behavior

Not following error tuples for decoding operations.

Expected behavior

Return {:error, :bad_encoding} instead of just :error

Most Liked

mgwidmann

mgwidmann

I think that because we have the freedom to return whatever we are allowing that to cloud our judgement a bit. In a statically typed language there wouldn’t even be an argument about this because the same type would be returned everywhere. There can most certainly be more information returned even though the actual issue is always the same. Consider this:

case Base.decode64(text) do
  {:ok, data} -> {:ok, data}
  {:error, {:bad_encoding, byte_offset}} -> {:error, "Encountered error decoding after the #{byte_offset}th byte\n#{text}"}
end

Both what went wrong and specifics about the error are accessible in native elixir terms and we can then choose to create a more friendly or debuggable error (or even take different action depending on the error metadata if we so choose).

Supposed we had a type to represent this as part of the standard library:

defmodule Result do
  @type success :: {:ok, any()}
  @type error :: {:error, {atom(), any()}}
  @type t :: success | error
end

This is so common in Erlang and Elixir. Why is there no type for it?

Now would the question really be what should we return? In order to be consistent it would become mandatory to return a Result.t() everywhere, even where not necessary, not only for consistency sake but for backwards compatibility. Now if we ever need to add another error condition, or some metadata about what went wrong, its easy to do so.

alexcastano

alexcastano

I think if you return a tuple is because you’re returning more information about the error. However, {:error, :bad_encoding} is not giving you more information than {:error}. If I would need this for a with, I would create a simple wrapper:

defmodule MyBase do
  def decode64(bin) do
    case Base.decode(bin) do
      :error -> {:error, :bad_encoding}
      any -> any
    end
  end
end
victorolinasc

victorolinasc

The more information, in this case, is that this is an error from a decoding function. We could go even further and provide {:error, :bad_base64_encoding} and so on.

Of course you can wrap the function and that is valid even for functions that DO return error tuples, but my point here is about how you instrospect errors. If you “know” beforehand this return is possible from decode functions, then sure, you can wrap it. But what if you don’t know this and has this log on your production system “Some error”?

So: :error is not as obvious as {:error, :bad_encoding} to my understanding at least. What I was hoping to achieve here is to provide just what you people are saying: being obvious about what happened in an error other than just saying “an error occured”.

In any case, it seems like I am alone in this quest hehehe that’s fine too. I thought this would turn out the other way around but it is not like there is a wrong or right here.

Where Next?

Popular in Discussions Top

Rustixir
Hi everyone, im working on find best language/framework/system for high concurrency, high performance and stable performance after wor...
New
arpan
Hello everyone :wave: Today I am very excited to announce a project that I have been working on for almost 3 months now. The project is...
New
Nvim
Elixir appears to be a superior language to Python. I don’t see any advantage of Python over Elixir. Are there any?
New
nburkley
AWS re:Invent is on at the moment with some interesting announcements. One new feature in particular is the Lambda Runtime API for AWS La...
New
WolfDan
After doing a port from a c++ library to my project in phoenix I’ve seen that I need a faster way to run this algorithm and I found this ...
New
tmbb
This is a post to discuss the new Phoenix LiveView functionality. From Chris’s talk, it appears that they generate all HTML on the serve...
342 18146 126
New
saif
Hello everyone, Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
und0ck3d
Hello everyone! A few days ago I’ve created a topic here about how people were creating CMSs with Elixir and Phoenix. I’ve been studying...
New
opsb
We’re considering our architecture from a viewpoint of scaling our traffic heavily over the next 6 months. Our current deployment is runn...
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New

Other popular topics Top

lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 39297 209
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
klo
Got a question about when to concat vs. prepending items to list then reversing to achieve appending. So i know lists boil down to [1 | ...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New

We're in Beta

About us Mission Statement