I have that ugly code multiple functions return {:ok, what_i_need } tuple. That is why i use anonymous functions is there better way handle :ok tuples?
EDIT: alternatively, you could also use a with expression:
with {:ok, time} <- Time.new(3, 2, 00),
{:ok, naive_date_time} <- NaiveDateTime.new(Date.utc_today(), time)
do
# use naive_date_time here
else
{:error, err} -> # handle any error
end
You can do this stuff as part of the comprehension:
{:ok, time} = Time.new(3, 2, 00)
for x <- 1..30,
date = Date.utc_today(),
{:ok, date_time} = NaiveDateTime.new(date, time) do
MyApp.create_something(%{date_time: date_time})
end
EDIT:
Assuming the operations youâre performing with dates arenât the same ones here. Otherwise, you could just define your datetime outside the comprehension.
Am i the only one who doesnât like :ok tuples? I prefer that when a function success just returns result without tuple, if fails traditional way {:error, reason}. Idk maybe there is a good reason for :ok tuples.
It would require you to handle the error case first in much of your code or create awkward pattern matches.
case do_something() do
{:error, reason} ->
# Handle error
value ->
# Success case
end
case do_something() do
value when is_integer(value) ->
# Success case
{:error, reason} ->
# Handle error
end
Sometime ago I also had that impression, " why I need to match a tuple when I just want to return the value" the problem is that what really happens when you do res = f() itâs actually doing a pattern matching, not the usual assignment, in that case you are accepting anything, an value or error, requiring you to do the pattern matching, which is more work. Now if you want the result or the process can blew up, some functions have a bang ! version which will raise if failed, like file = File.read!("foo.txt"), you can look up to that too
In my mind that is âsloppy typingâ - always returning a two element tuple where the consistently typed value of the first element is an indication of the type of the second value is much cleaner - and I suspect much better for pattern matching.
The :ok/:error tuple is a poor manâs implementation of Either. When used correctly it makes it easier to compose functions without having to specify explicit conditionals to deal with the errors - i.e. it enables railway-oriented programming (ROP).
But what if {:error, _} is a legit return value? How would you distinguish this from a real error?
Consider an Elixir Term Parser: Terms.parse("{:error, :badarg}").
If we were returning plain values on success but {:error, :badarg} on non-string input, we had a problem here.
But since it is common to return wrapped values in success cases, we can clearly and unambiguisly distinguish between {:ok, {:error, :badarg}} and {:error, :badarg}.
I do not understand your reasoning, seems to work:
You should look at expedeâs âExceptionalâ library on hex.pm then.
Yeah, I am really liking how Rust is implementing it actually.
That is when {:ok, ...} should always be used as the success value then.
You could always have done this if you really wanted it âinlineâ without using a function call:
# This:
(fn ->
{:ok, date_time} =
NaiveDateTime.new(
Date.utc_today(),
(fn ->
{:ok, time} = Time.new(3, 2, 00)
time
end).()
)
date_time
end).()
# Could be done as this (not really readable):
({:ok, dt} = NaiveDateTime.new(Date.utc_today(), ({:ok, t} = Time.new(3, 2, 00); t)); dt)
# Or as (if you *know* your inputs are valid as shown above):
elem(NaiveDateTime.new(Date.utc_today(), elem(Time.new(3, 2, 00), 1), 1)
# Or standalone bindings are always nicely readable:
( {:ok, time} = Time.new(3, 2, 00)
{:ok, date_time} = NaiveDateTime.new(Date.utc_today(), time)
date_time
# Although what really should be done is putting it into a function that you then just call:
get_offset_date_time() # :-)
Iâve adopted the {:ok, value} or {:error, error} (where error will always be an Exception) style completely for my libraries and many of my functions are now just with/else/end statements.
Iâve found its better to adopt the style (almost) 100% - switching between the two is just too confusing as you never remember whether the result will be :ok/:error style or the âbareâ value.
To support the style though Iâve had to write supporting functions, especially when Enum.map and the functions returns :ok/:error.
Depending on your taste the with/else/end may look awfully verbose or quite clean. After working with the style for a while now, I find the code looks quite âcleanâ: here for example is a module in one of my libraries.