31
votes

I'm working on a price format function, which takes a float, and represent it properly.

ex. 190.5, should be 190,50

This is what i came up with

  def format_price(price) do
    price
    |> to_string
    |> String.replace ".", ","
    |> String.replace ~r/,(\d)$/, ",\\1 0"
    |> String.replace " ", ""
  end

If i run the following.

format_price(299.0)
# -> 299,0

It looks like it only ran through the first replace. Now if i change this to the following.

  def format_price(price) do
    formatted = price
    |> to_string
    |> String.replace ".", ","

    formatted = formatted
    |> String.replace ~r/,(\d)$/, ",\\1 0"

    formatted = formatted
    |> String.replace " ", ""
  end

Then everything seems to work just fine.

format_price(299.0)
# -> 299,00

Why is this?

1
In general (for those who may read this later) when you're piping arguments through functions, make sure you put parentheses around your arguments. - Onorio Catenacci

1 Answers

32
votes

EDIT On the master branch of Elixir, the compiler will warn if a function is piped into without parentheses if there are arguments.


This is an issue of precedence that can be fixed with explicit brackets:

price
|> to_string
|> String.replace(".", ",")
|> String.replace(~r/,(\d)$/, ",\\1 0")
|> String.replace(" ", "")

Because function calls have a higher precedence than the |> operator your code is the same as:

price
|> to_string
|> String.replace(".",
  ("," |> String.replace ~r/,(\d)$/,
    (",\\1 0" |> String.replace " ", "")))

Which if we substitute the last clause:

price
|> to_string
|> String.replace(".",
  ("," |> String.replace ~r/,(\d)$/, ".\\10"))

And again:

price
|> to_string
|> String.replace(".", ",")

Should explain why you get that result.