Whatever floats your boat

In Javascript

One of my favourite talks of all time is WAT (by Gary Bernhardt). If you haven’t seen it yet, click that link. Seriously, I’ll be waiting right here.

Ok, ready? You watched that video? It’s amazing, right? Now, even when he shows some incredibly nonsensical Javascript oddities, considering he gave this talk on 2012, my all-time favourite WAT thing (which, of course, comes from Javascript) is not there. It was already fixed by that time.

About a decade ago, I used to be a Javascript developer. I’ll repeat that: I was a Javascript developer. Anyway, one otherwise unremarkable August 1st I found the weirdest bug in my code. The thing is, in JS back then, the following statements were true:

5 == parseInt("05");

6 == parseInt("06");

7 == parseInt("07");

but this one was not:

8 == parseInt("08");

Instead, these 2 were true:

0 == parseInt("08");

NaN == parseInt("8");

Gary Coleman (Different Strokes)

Back to Erlang

But I’m no longer a javascript developer, I’m an Erlang developer now. Surely such a thing won’t happen here, right?

Well, having a parseInt function that works in octal representation by default is not easy to match. But parsing ints and floats in Erlang caused me more than one headache already. Let me tell you why…

Parsing Integers

To parse integers in Erlang, you have erlang:list_to_integer/1. You can use it this way:

1> erlang:list_to_integer("08").

8

2>

What many Erlang devs may not know is that there is actually another way to do it:

1> string:to_integer("08").

{8, []}

2>

And with string:to_integer/1 your string can contain more things than just an integer:

1> erlang:list_to_integer(“8 and something”).

** exception error: bad argument

in function list_to_integer/1

called as list_to_integer(“8 and something”)

2> string:to_integer(“8 and something”).

{8,” and something”}

3>

Using that, you can extract the integer portion of a float, like this:

3> string:to_integer(“8.1234”).

{8,”.1234"}

Parsing Floats

Now, what if you want to actually parse floating point numbers? You have 2 functions again. Check it out:

1> list_to_float(“8.1234”).

8.1234

2> string:to_float(“8.1234 and more”).

{8.1234,” and more”}

3>

And that’s all, folks

Everything is fine up to this point. What annoys me just a bit is the fact that, even when integers are coerced (i.e. treated as floats) for all comparison operations besides =:= and =/=, and there is a built-in type number() that represents both integers and floats together, there is no simple way to parse a number in general. I think it will be clearer with an example:

1> erlang:list_to_float(“8”).

** exception error: bad argument

in function list_to_float/1

called as list_to_float(“8”)

2> string:to_float(“8”).

{error,no_float}

3>

I get it, “8” is technically not a float… but still 8 == 8.0, right? I would’ve expected these two operations to return true:

1> 8 == 8.0.

true

2> list_to_float(“8”) == list_to_float("8.0").

** exception error: bad argument

in function list_to_float/1

called as list_to_float("8")

3>

In any case, if that won’t work, I would love to have the following function on erlang:

-spec list_to_number(string()) -> number().

list_to_number(List) ->

try list_to_float(List)

catch

_:badarg -> list_to_integer(List)

end.

and maybe this one in string:

-spec to_number(string()) -> {number(), string()}.

to_number(String) ->

case to_float(String) of

{error, no_float} ->

case to_integer(String) of

{error, no_integer} -> {error, no_number};

Result -> Result

end;

{Float, Reminder} -> {Float, Reminder}

end.

What do you think, readers?