Is there a one liner option to get a tuple from a function and return it directly?

Hello,

Is there a one liner option to get a tuple from a function and return it directly ?

Example of 2 lines :

{:ok, book} = get_book(opts)
{:ok, book}

is there a way to return it directly ?

Thanks

1 Like

is enough since the tuple is the value of the fuction call. But maybe I misunderstood your question.

1 Like
{:ok, book} = get_book(opts); book

You might also look for ! function… like get_book!()

or maybe I don’t get it…

because it could be a one liner too…

get_book(opts)
1 Like

I am not sure I understood your question, but you don’t need to match the tuple if you just want to return it. This works:

# This function returns the tuple
def my_function do
  # …
  get_book(opts)
end
2 Likes

This code is not equivalent to the one in the original question, though.

The original one raises on e. g. {:error, :foo}, yours propagates it further.

@lkuty , @kokolegorille :arrow_up: :slight_smile:

5 Likes

{:ok, book} = get_book(opts) is the expression. Everything is an expression in Elixir. That said, you might just omit the second line.

def foo(opts) do
  {:ok, _} = get_book(opts)
end

The above would both ① raise if get_book/1 returned something unexpected and ② return the result of the match expression if there was a successful match.

9 Likes

@mudasobwa and if it was in a “widh” statement" ? We must have that line to return the tuple ?

with {:ok, book} <- get_book(opts) do
          {:ok, book}
        else
          {:error, _error} ->
          raise InvalidRequest
        end
1 Like

You can invert the with, i.e. match on and handle the error in the body. That will just return the ok result silently.

Be careful of sacrificing readability in the quest for less lines.

1 Like

Or just use case here. Much cleaner.

3 Likes

Generally speaking, one should not use with/1 statement with a single match and else clause (credo would even warn about this.)

Inverting a clause to avoid else as suggested by @cmo suffers from the same disease as just returning get_book(opts) result directly: it won’t raise if get_book/1 returned somewhat weird. Usually, we use case/2 for that kind of behaviour.

opts
|> get_book()
|> case do
  {:ok, _} = result -> result
  {:error, _error} -> raise InvalidRequest
end
1 Like
with {:error, _error} <- get_book(opts) do
  raise InvalidRequest
end
1 Like

This is the closest I could get to it being a 1 liner and having mostly the same functionality. The one difference is that it produces a different error.

opts |> get_book() |> then(fn {:ok, x} -> x end)

If you know you’ll never get an error, you could also do this:

opts |> get_book() |> elem(1)

I’d probably avoid using these in most situations though. In other languages with the result monad, they often have a function called unwrap, which will extract the value or raise an exception if the value is an error. I’d probably just define my own if I was doing this commonly, but there are a number of monadic libraries in Elixir that do something similar.

2 Likes

One more, here are some syntactic tricks to turn it into 1 line, though probably not what you’re looking for.

({:ok, _x} = get_books(opts)) |> elem(1)
1 Like