jarlah

jarlah

I see in Elixir that I can call Integer.parse! or Integer.parse right. Where the first one errors out if something is wrong, while the second one returns a result right. Or thats the general idea behind the exclamation mark after a method name.

So my question is, why can Integer.parse still fail?

If we look at Integer — Elixir v1.12.3 we see that the method is clearly documented to throw an exception “if base is less than 2 or more than 36.”

My question is, why? Why havent Elixir made the language so that you can choose between exceptions or results?

Showing Posts 1 to 10

jeroenbourgois

jeroenbourgois

A lot of times I use the ! variants if an error is considered unexpected behaviour. Then I want my app to crash, I don’t care about handling those cases. This makes code easier to read as far as I’m concerned.

Consider having a method where you retrieve a database records. Sometimes you want to be able to handle the fact the record is not there. But somethimes you know it is there; and if not the app can go into oblivion.

Furthermore, this can help with typespecs in your app, if you want to go down the ‘happy’ path. In our projects we make a distinction between errors that can occur and should be handled and unexpected behaviour where there is no sensible thing to do anyway. I hope my reply makes a bit of sense.

stefanchrobot

stefanchrobot

The way I think about it is this: there are expected and unexpected scenarios/errors; or to put it differently - some data is known/expected to be good and some needs validation.

(I think you meant String.to_integer instead of Integer.parse!, so let’s take DateTime.from_unix instead).

Let’s say I have a number which represents a timestamp and I want to convert it to a %DateTime{}:

  • If it’s an input from a user, I’d use DateTime.from_unix(number) to see if it’s correct because I expect the user to make mistakes or put invalid data,
  • If it’s a timestamp coming from an external API I’m expecting this to always be correct, so I’ll use DateTime.from_unix!(number) or {:ok, dt} = DateTime.from_unix(number) if a bang version is not available; I don’t want to litter my code with error handling - those code paths will never be run; obviously the timestamp might be wrong due to some sort of a glitch, but an answer to that is putting a higher-level measures for handling such situations (retrying a job, responding with proper status code, etc.).

From that perspective, for me, it makes sense that String.upcase(1) crashes. It’s my job to verify if what I’m passing is actually a string. I’d say that for Integer.parse/2 it’s more debatable what should happen when the second argument is invalid. I guess the behaviour is optimized for cases (which I would say is the majority) when the base is hardcoded.

If you’re writing something like a calculator and both arguments are coming from user input, then it’s pretty straightforward to build your own parse function (base not in 2..36).

SirWerto

SirWerto

I think it goes against the philosophy of “Let it crash”. If you have controlled exceptions, for example, timeout on database connection, file no found and so on, It’s totally fine to return an {:error, Reason}, but if you have some unexpected behaviours in your code, like “no function clause matching” which probably means that you have bad code somewhere, It’s easy to crash than returning the useless {:error, FunctionClauseError}.

Marcus

Marcus

See also Trailing bang (foo!) in the Elixir documentation.

The version without ! is preferred when you want to handle different outcomes using pattern matching:

case File.read(file) do
  {:ok, body}      -> # do something with the `body`
  {:error, reason} -> # handle the error caused by `reason`
end

However, if you expect the outcome to always to be successful (for instance, if you expect the file always to exist), the bang variation can be more convenient and will raise a more helpful error message (than a failed pattern match) on failure.

jarlah

jarlah OP

Looks like i have to change my mindset a little bit then. I mean I would still try to gracefully handle errors in a rest controller in phoenix. But the problem we are talking about here is maybe the toll it takes to consider every single corner case. However, think about this corner case:

The rest controller is called from a client, could be react, could be anything right. The called endpoint is called regularly, or quite often. If we expect certain conditions to be met or expect certains rows to exist etc, the process would die and be recreated over and over and over again.

So we have two ways of handling this scenario right:

  1. Handle the error and log it and return a sensible error to the caller
  2. Die and burn, and let the process be recreated.

Im not sure if im good with option #2. So thats the reason why i am bringing it up.

LostKobrakai

LostKobrakai

This expects a crash within the system does not result in a proper response at another place. That’s most often not the case though. Even if you don’t explicitly handle crashes on a web endpoint clients will receive a 500 status. If your client doesn’t react accordingly to that you’re back in #2 land, but I’d say that’s a problem of your client, not a problem on the server side. A 500 is maybe not a very detailed error response, but it is an error response.

cmo

cmo

Do you really want to cover every possible error case? You’ve got better things to do than worry about someone trying to parse an integer with a base of -1.

jarlah

jarlah OP

:joy:

No :slight_smile: It was merely an example on a symptom i saw in the language.

But its ok really, im just getting to terms with how things are done. Im not giving up on it yet, but for now I will put it to rest. I dont think its a good practice to avoid catching errors “because erlang platform handles process crashes”. But again the latter could be its own thread. So lets leave it at this.

stefanchrobot

stefanchrobot

I would treat crashing regularly and returning 500s as a sign of laziness or simply bugs. I’d go with option #1 for all the cases where I expect that something can go wrong. I think it’s a matter of your own judgment - is the specific corner case something that can and will happen or is it only a theoretical option. If it’s something that happens, then I’m all for handling it gracefully in the code at the cost of needing to write and maintain more code. There’s a bit of guesswork in deciding what needs to be handled explicitly, but you can always do a bit of “hardening” of the business logic.

eksperimental

eksperimental

IMHO in this particular case, Integer.parse/2 should raise FunctionClause error if base is not an integer or is an integer out of the 2..36 range, to mimic the rest of the language.

Where Next? Top

Trending in Questions Top

Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
Onor.io
I have what I’ve heard referred to as a “lookup table” in my database. This is a way of assigning codes to common values. One common lo...
New
Trolleger
What approach to take when sending live updates to “random” users Hi! I have a question, I have a little chat app, and when I create a DM...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
matt-savvy
Anyone here using Honeybadger? My Honeybadger account is being overwhelmed with noise from some bots. Seeing a lot of Bandit.HTTPError...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews