Struggling with having no early return

Hi,

I’m new to functional programming in general and to Elixir in particular. One thing I have some difficulties with is having no early return. I found This article, which works on some cases but not in one like this:

Let’s say I have 3 functions that can return either a value or nil:

get_text(:first)
get_text(:second)
get_text(:third)

I want to stop and get the result from the first function that returns a value (not nil) or nil if all fails.

Something like this works:

  def test_cases() do
    case get_text(:first) do
      value when value != nil ->
        value

      nil ->
        case get_text(:second) do
          value when value != nil ->
            value

          nil ->
            case get_text(:third) do
              value when value != nil ->
                value

              nil ->
                nil
            end
        end
    end
  end

but this looks really bad.

Using cond do works too and looks nicer but require calling each function twice:

  def text_cond() do
    cond do
      get_text(:first) != nil -> get_text(:first)
      get_text(:second) != nil -> get_text(:second)
      get_text(:third) != nil -> get_text(:third)
      true -> nil
    end
  end

I’m sure I’m missing something. Any help with this?

You can use || (assuming that these functions do not return boolean)

foo() || bar() || baz()

You can use with:

with nil <- foo(),
     nil <- bar(),
     nil <- baz(),
     do: nil
4 Likes

Perhaps controversial but I think this is perfect for a “failing with clause” (I forget what it’s actually called)

def text_cond() do
  with nil <- get_text(:first),
       nil <- get_text(:second) do
    get_text(:third)
  end
end

EDIT: I didn’t recommend the || solution as @hauleth showed, since your problem statement was about nil values, but definitely prefer that over the with.

3 Likes

Enum.find_value is an option:

Enum.find_value([:first, :second, :third], fn value -> get_text(value) != nil end)

Or, as per above, assuming get_text returns strings and no booleans:

Enum.find_value([:first, :second, :third], &get_text/1)

But ya, I’m just giving you more to think about. I would also go with ||.

EDIT: I originally suggested the wrong function :grimacing:

2 Likes

I think to have the same semantics you’d need to use Enum.find_value not Enum.find

Eep, my bad, yes. Corrected.

Thanks a lot. This works great.

Also thanks to all who respond.

Have a nice day.

4 Likes