Most elegant way for simple conditions an deconstructions

I would like to have your opinions about a very simple example, because I worked with imperative languages so far only, and I would like to get the mindset of declarative programming.

So i have a function that is called parse_index/1. During runtime we don’t know if we receive an integer or a string as argument, but if it is a string, we want to convert it to a number.
I wrote it like this:

  def parse_index(index) do
    is_number(index)
    |> case do
      true -> index
      false -> Integer.parse(index)
    end
    |> case do
      {number, _} -> number
      number -> number
    end
  end

These are a lot of lines for a pretty simple task, and I was wondering: how small and elegant can you rewrite that by taking advantage of more language features.
It’s really just to learn ‘how to think’ about problems why I ask that, because it kind of feels like I force the imperative way on Elixir when I write it like that…

Thanks!

Hello and welcome,

You could pattern match on function header…

like this

def parse_index(string) when is_binary(string) do
...
end
def parse_index(number), do: number
3 Likes

I generally tend to use ecto wherever data enters the system and converting it to a known format/type. E.g. using schemaless changesets:

def parse_something_with_an_index(data) do
  {%{index: nil}, %{index: :integer}}
  |> Ecto.Changeset.cast(data, [:index])
  |> Ecto.Changeset.validate_required([:index])
  |> …
end

changeset = parse_something_with_an_index(input)
case Ecto.Changeset.apply_action(changeset, :validate) do
  {:ok, data} -> # correct data
  {:error, changeset} -> # some errors
end

After that you can go on and do whatever you need to do with the data knowing it’s in the format type you expect it to be in.

3 Likes

In that case I would have:

  def parse_index(string) when is_binary(string) do
    Integer.parse(string)
    |> case do
      {number, _} -> number
      :error -> 0
    end
  end

  def parse_index(number), do: number

Then I would have something like this, where I return 0 in case I get a string that cannot be converted. Still wondering if I could / should get rid of the case after the pipe though

Interesting, I really need to take a closer look at Ecto. Thanks for the hint!

Pipe is nice, but too much is ugly :slight_smile:

  def parse_index(string) when is_binary(string) do
    case Integer.parse(string) do
      {number, _} -> number
      :error -> 0
    end
  end

… and if You really wanted to, You should have started with

def parse_index(string) when is_binary(string) do
    string
    |> Integer.parse()
    |> case do
      {number, _} -> number
      :error -> 0
    end
  end
2 Likes

I see, thanks! Prefer without pipes here too

2 Likes