Standard library suggested addition

Hi, still pretty new to Elixir. Is there a library equivalent of this? I’ve look but I haven’t found anything that looks right, but I want to make sure before I write a library to do it. It seems pretty general so I just assume it’s eluding me.

  # recursive get from collections
  defp getr(x, [head|tail]) when is_tuple(x) do
    getr(elem(x,head), tail)
  end
  defp getr(x, [head|tail]) when is_list(x) do
    getr(Enum.at(x,head), tail)
  end
  defp getr(x, [head|tail]) when is_map(x) do
    getr(Map.get(x,head), tail)
  end
  defp getr(x, []) do
    x
  end

This seems like an implementation of get_in/2.

5 Likes

Have a look at the Access behaviour.

etc.

iex(1)> users = [{"john", 27}, {"meg", 23}]
[{"john", 27}, {"meg", 23}]
iex(2)> keys = [Access.at(1), Access.elem(0)]
[#Function<1.79554587/3 in Access.at/1>,
 #Function<2.79554587/3 in Access.elem/1>]
iex(3)> get_in(users, keys)
"meg"
iex(4)>
1 Like

Ok. There it is I guess. I had seen get_in/2 and thought it would work at first, but the Access behaviour looked like it wouldn’t deal with lists and tuples, so I kept looking. I didn’t see the functions-as-keys approach. Seems clunkier than what I came up with, but I assume I’m missing something.
Thanks!

Functions also work with

and Access.key/2 can return a default value if the entry is not found.

There’s also Access.filter/1 and Access.all/0.

1 Like

The whole get_in/2 and update_in/3 are Elixir version of lenses which is functional structure for handling and manipulating nested structure. In it core functionality these are 2 functions:

  • get which extracts data from the structure
  • set which put data within structure

So get_in/2 can be thought as a list of functions that will extract value from structure in form:

get_in(stuct, [f, g])

# is (roughly) the same as

g(f(struct))

# and

update_in(struct, [f, g], val)

g(f(struct, val), val)

In reality it is “slightly” more complicated as you cannot return “closured object” with multiple functions in Elixir. So instead it works like that:

f =
  fn
    :get, struct, next -> # extract value and call `next` on it
    :update_in, struct, next -> # call next on data and then update `struct`
  end

So in the end, example implementation of Access.key!:

  def key!(key) do
    fn
      :get, %{} = data, next ->
        # call `next` on extracted data
        next.(Map.fetch!(data, key))

      :get_and_update, %{} = data, next ->
        # get "current data" under key
        value = Map.fetch!(data, key)

        # call `next` on current value
        case next.(value) do
          # return value and update structure
          {get, update} -> {get, Map.put(data, key, update)}
          # remove value from structure
          :pop -> {value, Map.delete(data, key)}
        end

      _op, data, _next ->
        raise "Access.key!/1 expected a map/struct, got: #{inspect(data)}"
    end
  end
2 Likes