It reduces a small amount of boilerplate in the original example but I agree it is not significant. My hope though is that it reduces the conceptual overhead of reaching to that solution in the first place.
However, I think there are examples where it reduces considerably. Today, as I worked, I tried to see examples where to apply it. Here are two.
This:
Enum.flat_map_reduce(subs, sources, fn sub, sources ->
sub_formatter = Path.join(sub, ".formatter.exs")
if File.exists?(sub_formatter) do
formatter_opts = eval_file_with_keyword_list(sub_formatter)
{formatter_opts_and_subs, sources} =
eval_deps_and_subdirectories(:in_memory, [sub], formatter_opts, sources)
{[{sub, formatter_opts_and_subs}], sources}
else
{[], sources}
end
end)
Can be rewritten to this:
for let(sources),
sub <- subs,
sub_formatter = Path.join(sub, ".formatter.exs"),
File.exists?(sub_formatter) do
formatter_opts = eval_file_with_keyword_list(sub_formatter)
{formatter_opts_and_subs, sources} =
eval_deps_and_subdirectories(:in_memory, [sub], formatter_opts, sources)
{{sub, formatter_opts_and_subs}, sources}
end
And this:
def find_asset_info(notebook, hash) do
Enum.find_value(notebook.sections, fn section ->
Enum.find_value(section.cells, fn cell ->
is_struct(cell, Cell.Elixir) &&
Enum.find_value(cell.outputs, fn
{:js_static, %{assets: %{hash: ^hash} = assets_info}, _data} -> assets_info
{:js_dynamic, %{assets: %{hash: ^hash} = assets_info}, _pid} -> assets_info
_ -> nil
end)
end)
end)
end
to this:
for reduce(value = nil),
value == nil,
section <- notebook.sections,
%Cell.Elixir{} = cell <- section.cells,
output <- cell.outputs do
case output do
{:js_static, %{assets: %{hash: ^hash} = assets_info}, _data} -> assets_info
{:js_dynamic, %{assets: %{hash: ^hash} = assets_info}, _pid} -> assets_info
_ -> nil
end
end
As soon as you get any kind of nesting, the comprehension format really starts to stand out.