Most idiomatic way to call fb() only if fa() succeeds - functional-programming

I have two functions, I want to run step_1, then run step_2 only if step_1 was ok. Whats the most functionally idiomatic way to structure this code? Nesting case statements seems ugly but I'm not sure how else to do it besides an if which is even worse.
defmodule Test do
def step_1 do
IO.puts "Step 1"
:error
end
def step_2 do
IO.puts "Step 2"
:ok
end
def do_something_case do
case step_1 do
:ok ->
case step_2 do
:ok -> {:ok}
:error -> {:error, :step_2}
end
:error -> {:error, :step_1}
end
end
def do_something_if do
r1 = step_1
if r1 == :ok
r2 = step_2
if r2 == :ok
:ok
else
{:error, :step_2}
end
else
{:error, :step_1}
end
end
end

A common way to solve this problem (without a monad library) is to pattern match on the arguments of the functions and pipe them together.
defmodule Test do
def step_1 do
IO.puts "Step 1"
:error
end
def step_2(:ok) do
IO.puts "Step 2"
:ok
end
def step_2({:error, _reason} = error) do
error
end
def do_something_pipeline do
step_1
|> step_2
end
end

Related

Check if all elements in list have a :ok element

I have one list of tuples like this
[
{:ok, {"0000 0000 "}},
{:ok, %{AM01: %{"C4" => "1111", "C5" => "1"}}},
{:ok, %{AM04: %{"C2" => "2222", "C6" => "2"}}}
]
The first element of tuple :ok represents the map ok.
How ca n I reduce this list to one list like this
[:ok, :ok, :ok]
because after this transformation I will check if all ok with Enum.all?
#JustinWood had demonstrated the most idiomatic erlang approach.
The most idiomatic elixir approach would be probably to use Kernel.match?/2, which is basically a syntactic sugar for two true/false clauses:
Enum.all?(input, &match?({:ok, _}, &1))
#⇒ true
Getting all :ok is usually done with Kernel.SpecialForms.for/1 list comprehension, which filters and maps in one loop:
for {:ok, _} <- input, do: :ok
#⇒ [:ok, :ok, :ok]
Instead of iterating through the list to convert to a list of atoms, you can just use Enum.all?/2 immediately.
result = [
{:ok, {"0000 0000 "}},
{:ok, %{AM01: %{"C4" => "1111", "C5" => "1"}}},
{:ok, %{AM04: %{"C2" => "2222", "C6" => "2"}}}
]
Enum.all?(result, fn
{:ok, _} -> true
_ -> false
end)
Using elem/2, you can reduce the map like this:
Enum.map(foo, &elem(&1, 0))
> [:ok, :ok, :ok]
Or just use all? directly:
Enum.all?(foo, &elem(&1, 0) == :ok)
> true
Another option:
def check_ok([]), do: true
def check_ok([{:ok, _} | tail]), do: check_ok(tail)
def check_ok(_), do: false
As long as :ok is found in each element of the list, the empty list will eventually match(clause #1) and return true. Otherwise, when something other than :ok is found, the recursion will end immediately and the function will return false(clause #3).

Elixir: recursive generators

Is it possible to build a Python style recursive generator with Elixir? Something like this:
def traverse(parent_dir):
dirs, files = get_dirs_and_files_as_lists(parent_dir)
for d in dirs:
yield from traverse(d)
for f in files:
yield f
For all the files to be processed linearly, without overhead implied by an eager list of indefinite length:
for f in traverse(dir):
process(f)
This, or some working equivalent, should be possible using streams; unfortunately, I have no idea how.
I want something like this, just lazy:
def traverse_eagerly(parent_dir) do
{dirs, files} = get_dirs_and_files_as_lists(parent_dir)
for x <- dirs do
traverse_eagerly(x)
end
|> Enum.concat()
|> Enum.concat(files)
end
The solution appears to be trivial: replace Enum with Stream.
def traverse_lazily(parent_dir) do
{dirs, files} = get_dirs_and_files_as_lists(parent_dir)
for x <- dirs do
traverse_lazily(x)
end
|> Stream.concat()
|> Stream.concat(files)
end
The following works as expected:
s = traverse_lazily(a_dir_of_choice)
for x <- s, do: whatever_you_please(x)
Very nice of the language. As fine a solution as you would wish for. Unless I'm missing something, that is :) . Comments are welcome!
You do not need Stream here, but if you want, here is it:
defmodule Traverse do
#spec traverse(root :: binary(), yielder :: (binary() -> any())) ::
:ok | {:error, posix()}
def traverse(root, yielder) do
# https://hexdocs.pm/elixir/master/File.html?#ls/1
with {:ok, list} <- File.ls(root) do
list
|> Stream.each(fn file_or_dir ->
if File.dir?(file_or_dir),
do: traverse(file_or_dir, yielder), # TCO
else: yielder.(file_or_dir)
end)
|> Stream.run()
end
end
end
And call it like:
Traverse.traverse(".", &IO.inspect/1)

async Elixir vs async Julia

In Elixir I can run code asynchronously like this
defmodule Async do
def example do
process_id = Task.async(fn ->
#some code you want its result later
end)
#do some stuff
async_result = Task.await(process_id)
end
end
and, if i don't need any results I can do like this
defmodule Async do
def example do
process_id = Task.start_link(fn ->
#some code you want its result later
end)
#do some stuff
:ok
end
end
What's the equivalent of above in Julia lang?
if you do not care about the result, you can use #async:
function foo()
sleep(100)
sum(1:100)
end
julia> #async foo()
Task (runnable) #0x00007f983b9d5f90
julia>
in the above example you get back the control of the terminal, without having to wait until the end of the execution of foo()
if you want to know the result, and keep an asynchronous behavior, you can use Task, schedule and fetch together:
julia> a() = sum(1:10000)
a (generic function with 1 method)
julia> b = Task(a)
Task (runnable) #0x00007f983b9d5cf0
julia> schedule(b)
Task (done) #0x00007f983b9d5cf0
julia> fetch(b)
50005000
Here is a small piece of code I use to illustrate the different behaviors:
module async
function example1()
a() = #async sleep(2)
b = Task(a)
schedule(b)
println(fetch(b))
sleep(4)
fetch(b)
end
function example2()
a() = sleep(2)
b = Task(a)
schedule(b)
fetch(b)
end
function example3()
a() = sum(1:10000)
b = Task(a)
schedule(b)
fetch(b)
end
end;
when I run this code, I get:
julia> async.example1()
Task (runnable) #0x00007f983b9d5510
Task (done) #0x00007f983b9d5510
julia> async.example2()
julia> async.example3()
50005000
async.example2() does not return any result, but keep the terminal busy around 2 seconds since fetch waits for the task to complete before giving back the hand.

Testing recursive IO prompt in Elixir/Erlang

I have a function confirm, which reads IO input, and depending by input, if it's y (yes) or n (no), it returns true/false, otherwise it calls confirm again, until any expected y or n is entered.
#spec confirm(binary) :: boolean
def confirm(question) do
answer = question |> IO.gets() |> String.trim() |> String.downcase()
case(answer) do
"y" -> true
"n" -> false
_ -> confirm(question)
end
end
To test y and n cases it's easy:
assert capture_io([input: "y"], fn -> confirm("Question") end) == "Question"
But I have no idea how to capture IO multiple times to test recursive case, let's say if at first input is "invalid" and then "y". Does elixir has any way to test IO functions like this? Or maybe do you have some suggestions how I could rewrite function to test it easier?
Original question https://elixirforum.com/t/testing-recursive-io-prompt/3715
Thanks for the help.
Untested, but what about just using newline characters?
assert capture_io([input: "foo\nbar\ny"], fn -> confirm("Question") end) == "Question"

tail recursive call in elixir and default parameters

I am writing a simple example in Elixir and although it works I don't really understand how.
defmodule MyList do
def sum([],acc \\ 0), do: acc
def sum([head | tail], acc), do: sum(tail,acc + head)
end
When I call MyList.sum I get the expected result
sum([]) => 0
sum([1,2,3]) => 6
I cannot add a default param in the second sum because the compiler throws an error
def sum/2 has default values and multiple clauses, use a separate clause for declaring defaults
So my question is, how come sum([1,2,3]) works? It does not match any of the definitions.
Is the function still tail recursive?
When you have a multiclause with optional arguments, you can specify defaults as a body-less clause:
defmodule MyList do
def sum(list, acc \\ 0) # sets up default arguments
def sum([],acc), do: acc
def sum([head | tail], acc), do: sum(tail,acc + head)
end
Regarding your example, I'm just guessing, but I think that your code amounts to something like following:
defmodule MyList do
# implicitly generated due to default argument
def sum(list), do: sum(list, 0)
def sum([],acc), do: acc
def sum([head | tail], acc), do: sum(tail,acc + head)
end
Which is why sum([1,2,3]) works as well.
Edit:
The function is definitely tail recursive. If the last thing a function does is a call of another function (or itself), then it is a tail call. So in this case, when we call sum(tail, acc + head), first the arguments are calculated, and then a tail recursive call happens.

Resources