Indeed it would, but that is only because of the current patterns that are used. Following your example but with a monadic style (from this thread) it would change from:
content =
Enum.find_value(sources, fn source ->
File.exists?(source) && File.read!(source)
end) || raise "could not find #{source_file_path} in any of the sources"
To if Enum.find_value
’s callback an option tuple ({:ok, blah}
|:error
) (using module helpers as stated should be made in the above linked thread):
{:ok, content} =
Enum.find_value(sources, &Option.from_result(File.read(&1)))
~> fn _ -> raise "could not find #{source_file_path} in any of the sources" end
# Or do `|> unwrap()` here and replace the binding above with just `content =`
And for the other example of:
Enum.find_value(headers, fn({k, v}) -> k =~ ~r/^origin$/i && v end)
To if Enum.find_value
’s callback an option tuple ({:ok, blah}
|:error
) (using module helpers as stated should be made in the above linked thread):
Enum.find_value(headers, fn({k, v}) -> Option.wrap_if(k =~ ~r/^origin$/i, v) end
And the third example of:
defp check_operations_valid(operations) do
Enum.find_value(operations, &invalid_operation/1) || {:ok, operations}
end
defp invalid_operation({name, {:changeset, %{valid?: false} = changeset, _}}),
do: {:error, {name, changeset, %{}}}
defp invalid_operation(_operation),
do: nil
To if Enum.find_value
’s callback an option tuple ({:ok, blah}
|:error
) (using module helpers as stated should be made in the above linked thread):
defp check_operations_valid(operations) do
Enum.find_value(operations, &invalid_operation/1)
|> Result.from_option(operations) # Takes the optional error value, only evaluated if an `:error`
|> Result.flip() # Flip the `:error` and `:ok` cases
end
defp invalid_operation({name, {:changeset, %{valid?: false} = changeset, _}}),
do: {:ok, {name, changeset, %{}}}
defp invalid_operation(_operation),
do: :error
Which although a bit longer I find more readable rather than the easily missed (since I did originally miss it) trailing || {:ok, operations}
bit.
Tagged Tuples are far superior, especially with conditional piping as well.
Entirely true, and gets back to what I say should be properly named return values. Although if we could drop the erlang heritage and big erlang style I’d say the option
type should be {:some, value}
/:none
and the result
type should be {:ok, value}
/{:error, result}
or something along those lines, but {:ok, value}
/:error
is definitely more erlang’y and there is a lot of history and code there.
I’ve also been for that too, doing the option as {value}
/:error
(or :none
or whatever) would make sense, but there is no precedence for that either.
That example would be significantly better with cond
, yet another reason I encourage either purging if
as-it-stands from the language, or forcing an else
clause, always. I really do not like this magically appearing nil
from invisible else
’s.
Agreed yep, hence why I want some construct that forces the else
to exist, no magically appearing nil
’s and such.
Which gets back to my wanting to fix the scoping brokenness. :-V