0
votes

In Programming Phoenix, chapter 3, there is a hardcoded repo example with the following code:

defmodule Rumbl.Repo do
  @moduledoc """
  In memory repository.
  """

  def all(Rumbl.User) do
    [%Rumbl.User{id: "1", name: "José", username: "josevalim", password: "elixir"},
     %Rumbl.User{id: "2", name: "Bruce", username: "redrapids", password: "7langs"},
     %Rumbl.User{id: "3", name: "Chris", username: "chrismccord", password: "phx"}]
  end

  def all(_module), do: []

  def get(module, id) do
    Enum.find all(module), fn map -> map.id == id end
  end

  def get_by(module, params) do
    Enum.find all(module), fn map ->
      Enum.all?(params, fn {key, val} -> Map.get(map, key) == val end)
    end
  end
end

Using a pipe, I rewrote get/2 in a way that seems easier to understand:

  def get(module, id) do
    all(module)
    |> Enum.find fn map -> map.id == id end
  end

Is there a simple way to do the same to get_by/2?

2

2 Answers

3
votes

My thoughts are that in this case, the clarity would more likely be achieved, not by the pipeline operator itself, but giving descriptive names to the two different functions passed to the different Enum functions, instead of using the anonymous function syntax.

def get_by(module, params) do
  Enum.find all(module), is_superset_of?(params)
end

defp is_superset_of?(key_value_pairs) do
  fn map -> Enum.all?(key_value_pairs, key_value_pair_in?(map)) end
end

defp key_value_pair_in?(map) do
  fn {key, value} -> Map.get(map, key) == value end
end

This is done by taking advantage of the fact that functions can return functions, and close over the values passed in. This allows us to give a name to a function that represents the previous function that was anonymous (unnamed) and inlined to take advantage of the ability to close over the values needed.

We are now able to sill capture the closed over values, but at the same time increase the expressiveness of what we are trying to accomplish.

All this, assuming that I did interpret the intent of the anonymous functions accurately, as well. ;)

2
votes

if you wanted, you could do:

def get_by(module, params) do
  module
  |> all()
  |> Enum.find(fn map ->
    Enum.all?(params, fn {key, val} -> Map.get(map, key) == val end)
  end)
end