summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrea Leopardi <an.leopardi@gmail.com>2018-05-11 00:45:35 +0200
committerAndrea Leopardi <an.leopardi@gmail.com>2018-05-11 00:45:54 +0200
commit19105a3bd2437250113776164ddf1209a65da387 (patch)
tree7ab15ce867f2ca5f6955d380634727f2a9f0b47b
parentd1e3f3acd2ebbc5df7796c22e5b8b37dabadbd59 (diff)
downloadelixir-19105a3bd2437250113776164ddf1209a65da387.tar.gz
Polish docs around Access and Access-related functions in Kernel
-rw-r--r--lib/elixir/lib/access.ex87
-rw-r--r--lib/elixir/lib/kernel.ex41
2 files changed, 83 insertions, 45 deletions
diff --git a/lib/elixir/lib/access.ex b/lib/elixir/lib/access.ex
index e8ff7222b..bfa455fb7 100644
--- a/lib/elixir/lib/access.ex
+++ b/lib/elixir/lib/access.ex
@@ -2,33 +2,33 @@ defmodule Access do
@moduledoc """
Key-based access to data structures.
- Elixir supports three main key-value constructs, keywords,
- maps and structs, and two mechanisms to access those keys,
- by brackets via `data[key]`, and by dot-syntax, via `data.field`.
+ Elixir supports three main key-value constructs: keywords,
+ maps, and structs. It also supports two mechanisms to access those keys:
+ by brackets (via `data[key]`) and by dot-syntax (via `data.field`).
- Next we will briefly recap the key-value constructs and then
+ In the next section we will briefly recap the key-value constructs and then
discuss the access mechanisms.
## Key-value constructs
Elixir provides three main key-value constructs, summarized below:
- * keyword lists - they are lists of two element tuples where
+ * keyword lists - they are lists of two-element tuples where
the first element is an atom. Commonly written in the
`[key: value]` syntax, they support only atom keys. Keyword
lists are used almost exclusively to pass options to functions
and macros. They keep the user ordering and allow duplicate
- keys. Powered by the `Keyword` module.
+ keys. See the `Keyword` module.
- * maps - they are the "go to" key-value data structure in Elixir
- capable of supporting billions of keys of any type. They are
- written in the `%{key => value}` syntax and also support the
+ * maps - they are the "go to" key-value data structure in Elixir.
+ They are capable of supporting billions of keys of any type. They are
+ written using the `%{key => value}` syntax and also support the
`%{key: value}` syntax when the keys are atoms. They do not
have any specified ordering and do not allow duplicate keys.
- Powered by the `Map` module.
+ See the `Map` module.
* structs - they are named maps with a pre-determined set of keys.
- They are defined with `defstruct/1` and written in the
+ They are defined with `defstruct/1` and written using the
`%StructName{key: value}` syntax.
## Key-based accessors
@@ -40,7 +40,8 @@ defmodule Access do
The `data[key]` syntax is used to access data structures with a
dynamic number of keys, such as keywords and maps. The key can
- be of any type and it returns nil if the key does not exist:
+ be of any type. The bracket-based access syntax returns `nil`
+ if the key does not exist:
iex> keywords = [a: 1, b: 2]
iex> keywords[:a]
@@ -62,12 +63,17 @@ defmodule Access do
iex> put_in(users["john"][:age], 28)
%{"john" => %{age: 28}, "meg" => %{age: 23}}
- Furthermore, the bracket access transparently ignores `nil` values:
+ Furthermore, the bracket-based access syntax transparently ignores
+ `nil` values. When trying to access anything on a `nil` value, `nil`
+ is returned:
iex> keywords = [a: 1, b: 2]
iex> keywords[:c][:unknown]
nil
+ iex> nil[:a]
+ nil
+
Internally, `data[key]` translates to `Access.get(term, key, nil)`.
Developers interested in implementing their own key-value data
structures can implement the `Access` behaviour to provide the
@@ -77,8 +83,8 @@ defmodule Access do
### Dot-based syntax
The `data.field` syntax is used exclusively to access atom fields
- in maps and structs. If the field accessed does not exist, it
- raises an error. This is a deliberate decision: since all of the
+ in maps and structs. If the accessed field does not exist, an error is
+ raised. This is a deliberate decision: since all of the
fields in a struct are pre-determined, structs support only the
dot-based syntax and not the access one.
@@ -107,22 +113,20 @@ defmodule Access do
## Nested data structures
Both key-based access syntaxes can be used with the nested update
- functions in `Kernel`, such as `Kernel.get_in/2`, `Kernel.put_in/3`,
- `Kernel.update_in/3` and `Kernel.get_and_update_in/3`.
+ functions and macros in `Kernel`, such as `Kernel.get_in/2`, `Kernel.put_in/3`,
+ `Kernel.update_in/3`, `Kernel.pop_in/2`, and `Kernel.get_and_update_in/3`.
- For example, to update a map inside another map of dynamic values,
- one can do:
+ For example, to update a map inside another map:
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
- iex> put_in users["john"].age, 28
+ iex> put_in(users["john"].age, 28)
%{"john" => %{age: 28}, "meg" => %{age: 23}}
-
This module provides convenience functions for traversing other
- structures, like tuples and lists, to be used alongside `Kernel.put_in/2`
- and others.
+ structures, like tuples and lists. These functions can be used
+ in all the `Access`-related functions and macros in `Kernel`.
- For instance, given a user map with `:name` and `:languages` keys,
+ For instance, given a user map with the `:name` and `:languages` keys,
here is how to deeply traverse the map and convert all language names
to uppercase:
@@ -190,7 +194,7 @@ defmodule Access do
def get(structure, key, default) do
case fetch(structure, key) do
{:ok, value} -> value
- :error -> default
+ :error -> default
end
end
@@ -208,9 +212,12 @@ defmodule Access do
If the passed function returns `{get_value, update_value}`,
the return value of this callback should be `{get_value, new_data}`, where:
- - `get_value` is the retrieved value (which can be operated on before being returned)
- - `update_value` is the new value to be stored under `key`
- - `new_data` is `data` after updating the value of `key` with `update_value`.
+
+ * `get_value` is the retrieved value (which can be operated on before being returned)
+
+ * `update_value` is the new value to be stored under `key`
+
+ * `new_data` is `data` after updating the value of `key` with `update_value`.
If the passed function returns `:pop`, the return value of this callback
must be `{value, new_data}` where `value` is the value under `key`
@@ -258,6 +265,15 @@ defmodule Access do
Returns `{:ok, value}` where `value` is the value under `key` if there is such
a key, or `:error` if `key` is not found.
+
+ ## Examples
+
+ iex> Access.fetch(%{name: "meg", age: 26}, :name)
+ {:ok, "meg"}
+
+ iex> Access.fetch([ordered: true, on_timeout: :exit], :timeout)
+ :error
+
"""
@spec fetch(container, term) :: {:ok, term} | :error
@spec fetch(nil_container, any) :: :error
@@ -299,6 +315,17 @@ defmodule Access do
Returns the value under `key` if there is such a key, or `default` if `key` is
not found.
+
+ ## Examples
+
+ iex> Access.get(%{name: "john"}, :name, "default name")
+ "john"
+ iex> Access.get(%{name: "john"}, :age, 25)
+ 25
+
+ iex> Access.get([ordered: true], :timeout)
+ nil
+
"""
@spec get(container, term, term) :: term
@spec get(nil_container, any, default) :: default when default: var
@@ -437,8 +464,8 @@ defmodule Access do
The returned function uses the default value if the key does not exist.
This can be used to specify defaults and safely traverse missing keys:
- iex> get_in(%{}, [Access.key(:user, %{}), Access.key(:name)])
- nil
+ iex> get_in(%{}, [Access.key(:user, %{name: "meg"}), Access.key(:name)])
+ "meg"
Such is also useful when using update functions, allowing us to introduce
values as we traverse the data structure for updates:
diff --git a/lib/elixir/lib/kernel.ex b/lib/elixir/lib/kernel.ex
index b16a73710..673ba9f17 100644
--- a/lib/elixir/lib/kernel.ex
+++ b/lib/elixir/lib/kernel.ex
@@ -2060,8 +2060,8 @@ defmodule Kernel do
iex> get_in(users, ["unknown", :age])
nil
- When one of the keys is a function, the function is invoked.
- In the example below, we use a function to get all the maps
+ When one of the keys is a function that takes three arguments, the function
+ is invoked. In the example below, we use a function to get all the maps
inside a list:
iex> users = [%{name: "john", age: 27}, %{name: "meg", age: 23}]
@@ -2132,23 +2132,29 @@ defmodule Kernel do
@doc """
Gets a value and updates a nested structure.
- `data` is a nested structure (ie. a map, keyword
+ `data` is a nested structure (that is, a map, keyword
list, or struct that implements the `Access` behaviour).
The `fun` argument receives the value of `key` (or `nil` if `key`
- is not present) and must return a two-element tuple: the "get" value
- (the retrieved value, which can be operated on before being returned)
- and the new value to be stored under `key`. The `fun` may also
- return `:pop`, implying the current value shall be removed
- from the structure and returned.
+ is not present) and must return one of the following values:
+
+ * a two-element tuple `{get_value, new_value}`. In this case,
+ `get_value` is the retrieved value which can possibly be operated on before
+ being returned. `new_value` is the new value to be stored under `key`.
- It uses the `Access` module to traverse the structures
+ * `:pop`, which implies that the current value under `key`
+ should be removed from the structure and returned.
+
+ This function uses the `Access` module to traverse the structures
according to the given `keys`, unless the `key` is a
function.
If a key is a function, the function will be invoked
- passing three arguments, the operation (`:get_and_update`),
- the data to be accessed, and a function to be invoked next.
+ passing three arguments:
+
+ * the operation (`:get_and_update`)
+ * the data to be accessed
+ * a function to be invoked next
This means `get_and_update_in/3` can be extended to provide
custom lookups. The downside is that functions cannot be stored
@@ -2158,8 +2164,8 @@ defmodule Kernel do
This function is useful when there is a need to retrieve the current
value (or something calculated in function of the current value) and
- update it at the same time. For example, it could be used to increase
- the age of a user by one and return the previous age in one pass:
+ update it at the same time. For example, it could be used to read the
+ current age of a user while increasing it by one in one pass:
iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_and_update_in(users, ["john", :age], &{&1, &1 + 1})
@@ -2171,7 +2177,7 @@ defmodule Kernel do
iex> users = [%{name: "john", age: 27}, %{name: "meg", age: 23}]
iex> all = fn :get_and_update, data, next ->
- ...> Enum.map(data, next) |> :lists.unzip
+ ...> data |> Enum.map(next) |> Enum.unzip()
...> end
iex> get_and_update_in(users, [all, :age], &{&1, &1 + 1})
{[27, 23], [%{name: "john", age: 28}, %{name: "meg", age: 24}]}
@@ -2182,7 +2188,7 @@ defmodule Kernel do
The `Access` module ships with many convenience accessor functions,
like the `all` anonymous function defined above. See `Access.all/0`,
- `Access.key/2` and others as examples.
+ `Access.key/2`, and others as examples.
"""
@spec get_and_update_in(
structure :: Access.t(),
@@ -2222,6 +2228,11 @@ defmodule Kernel do
In case any entry returns `nil`, its key will be removed
and the deletion will be considered a success.
+
+ iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
+ iex> pop_in(users, ["jane", :age])
+ {nil, %{"john" => %{age: 27}, "meg" => %{age: 23}}}
+
"""
@spec pop_in(data, nonempty_list(Access.get_and_update_fun(term, data) | term)) :: {term, data}
when data: Access.container()