View Source Bindable.ForComprehension (Bindable v1.1.0)

Elixir for-comprehension that goes beyond the Lists.

For-comprehension (its operations) could be implemented in several ways. For example, you can desugar it into chain of flatMap + map invocation:

  iex> xs = [1, 2]
  ...> ys = [3, 4]
  ...> for(x <- xs, y <- ys, do: {x, y}) === Enum.flat_map(xs, fn x -> Enum.map(ys, fn y -> {x, y} end) end)
  true

But if you try to implement assigns using flatMap + map, you will end up with doing something like map(ma, &{&1, assign}), and then each guard, that doesn't utilize every variable of such tuple, would raise an unused variable compiler warning (filter(ma, fn {a, b} -> a > 42 end)). However, suppressing all these warning would be a bad idea. They are useful to detect several kinds of flaws, while working with for-comprehension. For example inefficient guards:

  iex> import Bindable.ForComprehension
  ...> bindable for x <- [1, 2], y <- [3, 4], x < 2, do: {x, y}
  [{1, 3}, {1, 4}]

In this case the guard does not refer to the last generated value, so the expression can be refactored to apply the guard before the last generator. It can dramatically improve performance of the whole expression in general!

  iex> import Bindable.ForComprehension
  ...> bindable for x <- [1, 2], x < 2, y <- [3, 4], do: {x, y}
  [{1, 3}, {1, 4}]

That is why flatMap + pure was used for implementation, so assigns could be implemented in a most straightforward way, while preserving all compiler warnings. Compiler would warn you (about unused variable), if you try to apply the guard, which does not refer to the last generated value.

Link to this section Summary

Link to this section Functions

Elixir for-comprehension that goes beyond the Lists.

Elixir does not have variadic functions or macros. But Elixir's for-comprehension looks exactly like variadic macro:

  iex> for(x <- [1, 2], y <- [3, 4], do: {x, y})
  [{1, 3}, {1, 4}, {2, 3}, {2, 4}]

That's because it is actually a Kernel.SpecialForms.for/1, treated "specially" by compiler. So to emulate variadic nature of for-comprehension (e.g. it can have one or many generators) macro application was used (macro applied to Kernel.SpecialForms.for/1 expression):

  iex> import Bindable.ForComprehension
  ...> bindable for x <- [1, 2], y <- [3, 4], do: {x, y}
  [{1, 3}, {1, 4}, {2, 3}, {2, 4}]

It also supports Kernel.SpecialForms.for/1-like guards and assigns.

Link to this macro

bindable(arg, list)

View Source (macro)
Link to this macro

do_for(reverse_ordered_assigns, reverse_ordered_guards, generated_value_pattern, generator, list)

View Source (macro)

"Private" macro to implement for/2.

Due to Elixir's macro expand process nature you can't have this do_for macro as a "private" one. It would require do_for import at the call-site, which is impossible for "private"-macro (defined by defmacrop).