Add `@spec` for `Keyword.pop/3` and `Keyword.pop_first/3`

defmodule Keyword do
  @moduledoc """
  A keyword is a list of tuples where the first element
  of the tuple is an atom and the second element can be
  any value.

  A keyword may have duplicated keys so it is not strictly
  a dictionary. However most of the functions in this module
  behave exactly as a dictionary and mimic the API defined
  by the `Dict` behaviour.

  For example, `Keyword.get/3` will get the first entry matching
  the given key, regardless if duplicated entries exist.
  Similarly, `Keyword.put/3` and `Keyword.delete/3` ensure all
  duplicated entries for a given key are removed when invoked.

  A handful of functions exist to handle duplicated keys, in
  particular, `Enum.into/2` allows creating new keywords without
  removing duplicated keys, `get_values/2` returns all values for
  a given key and `delete_first/2` deletes just one of the existing
  entries.

  The functions in Keyword do not guarantee any property when
  it comes to ordering. However, since a keyword list is simply a
  list, all the operations defined in `Enum` and `List` can be
  applied too, specially when ordering is required.
  """

  @compile :inline_list_funcs
  @behaviour Dict

  @type key :: atom
  @type value :: any

  @type t :: [{key, value}]
  @type t(value) :: [{key, value}]

  @doc """
  Checks if the given argument is a keyword list or not.
  """
  @spec keyword?(term) :: boolean
  def keyword?([{key, _value} | rest]) when is_atom(key) do
    keyword?(rest)
  end

  def keyword?([]),     do: true
  def keyword?(_other), do: false

  @doc """
  Returns an empty keyword list, i.e. an empty list.
  """
  @spec new :: t
  def new do
    []
  end

  @doc """
  Creates a keyword from an enumerable.

  Duplicated entries are removed, the latest one prevails.
  Unlike `Enum.into(enumerable, [])`,
  `Keyword.new(enumerable)` guarantees the keys are unique.

  ## Examples

      iex> Keyword.new([{:b, 1}, {:a, 2}])
      [a: 2, b: 1]

  """
  @spec new(Enum.t) :: t
  def new(pairs) do
    Enum.reduce pairs, [], fn {k, v}, keywords ->
      put(keywords, k, v)
    end
  end

  @doc """
  Creates a keyword from an enumerable via the transformation function.

  Duplicated entries are removed, the latest one prevails.
  Unlike `Enum.into(enumerable, [], fun)`,
  `Keyword.new(enumerable, fun)` guarantees the keys are unique.

  ## Examples

      iex> Keyword.new([:a, :b], fn (x) -> {x, x} end) |> Enum.sort
      [a: :a, b: :b]

  """
  @spec new(Enum.t, ({key, value} -> {key, value})) :: t
  def new(pairs, transform) do
    Enum.reduce pairs, [], fn i, keywords ->
      {k, v} = transform.(i)
      put(keywords, k, v)
    end
  end

  @doc """
  Gets the value for a specific `key`.

  If `key` does not exist, return the default value (`nil` if no default value).

  If duplicated entries exist, the first one is returned.
  Use `get_values/2` to retrieve all entries.

  ## Examples

      iex> Keyword.get([a: 1], :a)
      1

      iex> Keyword.get([a: 1], :b)
      nil

      iex> Keyword.get([a: 1], :b, 3)
      3

  """
  @spec get(t, key) :: value
  @spec get(t, key, value) :: value
  def get(keywords, key, default \\ nil) when is_list(keywords) and is_atom(key) do
    case :lists.keyfind(key, 1, keywords) do
      {^key, value} -> value
      false -> default
    end
  end

  @doc """
  Gets the value for a specific `key`.

  If `key` does not exist, lazily evaluates `fun` and returns its result.

  This is useful if the default value is very expensive to calculate or
  generally difficult to set-up and tear-down again.

  If duplicated entries exist, the first one is returned.
  Use `get_values/2` to retrieve all entries.

  ## Examples

      iex> keyword = [a: 1]
      iex> fun = fn ->
      ...>   # some expensive operation here
      ...>   :result
      ...> end
      iex> Keyword.get_lazy(keyword, :a, fun)
      1
      iex> Keyword.get_lazy(keyword, :b, fun)
      :result

  """
  @spec get_lazy(t, key, (() -> value)) :: value
  def get_lazy(keywords, key, fun)
      when is_list(keywords) and is_atom(key) and is_function(fun, 0) do
    case :lists.keyfind(key, 1, keywords) do
      {^key, value} -> value
      false -> fun.()
    end
  end

  @doc """
  Gets the value from `key` and updates it, all in one pass.

  This `fun` argument receives the value of `key` (or `nil` if `key`
  is not present) and must return a two-elements 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 returned value is a tuple with the "get" value returned by `fun` and a new
  keyword list with the updated value under `key`.

  ## Examples

      iex> Keyword.get_and_update [a: 1], :a, fn(current_value) ->
      ...>   {current_value, "new value!"}
      ...> end
      {1, [a: "new value!"]}

  """
  @spec get_and_update(t, key, (value -> {value, value})) :: {value, t}
  def get_and_update(keywords, key, fun)
    when is_list(keywords) and is_atom(key),
    do: get_and_update(keywords, [], key, fun)

  defp get_and_update([{key, value}|t], acc, key, fun) do
    {get, new_value} = fun.(value)
    {get, :lists.reverse(acc, [{key, new_value}|t])}
  end

  defp get_and_update([h|t], acc, key, fun) do
    get_and_update(t, [h|acc], key, fun)
  end

  defp get_and_update([], acc, key, fun) do
    {get, update} = fun.(nil)
    {get, [{key, update}|List.reverse(acc)]}
  end

  @doc """
  Fetches the value for a specific `key` and returns it in a tuple.

  If the `key` does not exist, returns `:error`.

  ## Examples

      iex> Keyword.fetch([a: 1], :a)
      {:ok, 1}

      iex> Keyword.fetch([a: 1], :b)
      :error

  """
  @spec fetch(t, key) :: {:ok, value} | :error
  def fetch(keywords, key) when is_list(keywords) and is_atom(key) do
    case :lists.keyfind(key, 1, keywords) do
      {^key, value} -> {:ok, value}
      false -> :error
    end
  end

  @doc """
  Fetches the value for specific `key`.

  If `key` does not exist, a `KeyError` is raised.

  ## Examples

      iex> Keyword.fetch!([a: 1], :a)
      1

      iex> Keyword.fetch!([a: 1], :b)
      ** (KeyError) key :b not found in: [a: 1]

  """
  @spec fetch!(t, key) :: value | no_return
  def fetch!(keywords, key) when is_list(keywords) and is_atom(key) do
    case :lists.keyfind(key, 1, keywords) do
      {^key, value} -> value
      false -> raise(KeyError, key: key, term: keywords)
    end
  end

  @doc """
  Gets all values for a specific `key`.

  ## Examples

      iex> Keyword.get_values([a: 1, a: 2], :a)
      [1,2]

  """
  @spec get_values(t, key) :: [value]
  def get_values(keywords, key) when is_list(keywords) and is_atom(key) do
    fun = fn
      {k, v} when k === key -> {true, v}
      {_, _} -> false
    end

    :lists.filtermap(fun, keywords)
  end

  @doc """
  Returns all keys from the keyword list.

  Duplicated keys appear duplicated in the final list of keys.

  ## Examples

      iex> Keyword.keys([a: 1, b: 2])
      [:a,:b]

      iex> Keyword.keys([a: 1, b: 2, a: 3])
      [:a,:b,:a]

  """
  @spec keys(t) :: [key]
  def keys(keywords) when is_list(keywords) do
    :lists.map(fn {k, _} -> k end, keywords)
  end

  @doc """
  Returns all values from the keyword list.

  ## Examples

      iex> Keyword.values([a: 1, b: 2])
      [1,2]

  """
  @spec values(t) :: [value]
  def values(keywords) when is_list(keywords) do
    :lists.map(fn {_, v} -> v end, keywords)
  end

  @doc """
  Deletes the entries in the keyword list for a `key` with `value`.

  If no `key` with `value` exists, returns the keyword list unchanged.

  ## Examples

      iex> Keyword.delete([a: 1, b: 2], :a, 1)
      [b: 2]

      iex> Keyword.delete([a: 1, b: 2, a: 3], :a, 3)
      [a: 1, b: 2]

      iex> Keyword.delete([b: 2], :a, 5)
      [b: 2]

  """
  @spec delete(t, key, value) :: t
  def delete(keywords, key, value) when is_list(keywords) and is_atom(key) do
    :lists.filter(fn {k, v} -> k != key or v != value end, keywords)
  end

  @doc """
  Deletes the entries in the keyword list for a specific `key`.

  If the `key` does not exist, returns the keyword list unchanged.
  Use `delete_first/2` to delete just the first entry in case of
  duplicated keys.

  ## Examples

      iex> Keyword.delete([a: 1, b: 2], :a)
      [b: 2]

      iex> Keyword.delete([a: 1, b: 2, a: 3], :a)
      [b: 2]

      iex> Keyword.delete([b: 2], :a)
      [b: 2]

  """
  @spec delete(t, key) :: t
  def delete(keywords, key) when is_list(keywords) and is_atom(key) do
    :lists.filter(fn {k, _} -> k != key end, keywords)
  end

  @doc """
  Deletes the first entry in the keyword list for a specific `key`.

  If the `key` does not exist, returns the keyword list unchanged.

  ## Examples

      iex> Keyword.delete_first([a: 1, b: 2, a: 3], :a)
      [b: 2, a: 3]

      iex> Keyword.delete_first([b: 2], :a)
      [b: 2]

  """
  @spec delete_first(t, key) :: t
  def delete_first(keywords, key) when is_list(keywords) and is_atom(key) do
    :lists.keydelete(key, 1, keywords)
  end

  @doc """
  Puts the given `value` under `key`.

  If a previous value is already stored, all entries are
  removed and the value is overridden.

  ## Examples

      iex> Keyword.put([a: 1, b: 2], :a, 3)
      [a: 3, b: 2]

      iex> Keyword.put([a: 1, b: 2, a: 4], :a, 3)
      [a: 3, b: 2]

  """
  @spec put(t, key, value) :: t
  def put(keywords, key, value) when is_list(keywords) and is_atom(key) do
    [{key, value}|delete(keywords, key)]
  end

  @doc """
  Evaluates `fun` and puts the result under `key`
  in keyword list unless `key` is already present.

  This is useful if the value is very expensive to calculate or generally
  difficult to set-up and tear-down again.

  ## Examples

      iex> keyword = [a: 1]
      iex> fun = fn ->
      ...>   # some expensive operation here
      ...>   3
      ...> end
      iex> Keyword.put_new_lazy(keyword, :a, fun)
      [a: 1]
      iex> Keyword.put_new_lazy(keyword, :b, fun)
      [b: 3, a: 1]

  """
  @spec put_new_lazy(t, key, (() -> value)) :: t
  def put_new_lazy(keywords, key, fun)
      when is_list(keywords) and is_atom(key) and is_function(fun, 0) do
    case :lists.keyfind(key, 1, keywords) do
      {^key, _} -> keywords
      false -> [{key, fun.()}|keywords]
    end
  end

  @doc """
  Puts the given `value` under `key` unless the entry `key`
  already exists.

  ## Examples

      iex> Keyword.put_new([a: 1], :b, 2)
      [b: 2, a: 1]

      iex> Keyword.put_new([a: 1, b: 2], :a, 3)
      [a: 1, b: 2]

  """
  @spec put_new(t, key, value) :: t
  def put_new(keywords, key, value) when is_list(keywords) and is_atom(key) do
    case :lists.keyfind(key, 1, keywords) do
      {^key, _} -> keywords
      false -> [{key, value}|keywords]
    end
  end

  @doc """
  Checks if two keywords are equal.

  Two keywords are considered to be equal if they contain
  the same keys and those keys contain the same values.

  ## Examples

      iex> Keyword.equal?([a: 1, b: 2], [b: 2, a: 1])
      true

  """
  @spec equal?(t, t) :: boolean
  def equal?(left, right) when is_list(left) and is_list(right) do
    :lists.sort(left) == :lists.sort(right)
  end

  @doc """
  Merges two keyword lists into one.

  If they have duplicated keys, the one given in the second argument wins.

  ## Examples

      iex> Keyword.merge([a: 1, b: 2], [a: 3, d: 4])
      [a: 3, d: 4, b: 2]

  """
  @spec merge(t, t) :: t
  def merge(d1, d2) when is_list(d1) and is_list(d2) do
    fun = fn {k, _v} -> not has_key?(d2, k) end
    d2 ++ :lists.filter(fun, d1)
  end

  @doc """
  Merges two keyword lists into one.

  If they have duplicated keys, the given function is invoked to solve conflicts.

  ## Examples

      iex> Keyword.merge([a: 1, b: 2], [a: 3, d: 4], fn (_k, v1, v2) ->
      ...>  v1 + v2
      ...> end)
      [a: 4, b: 2, d: 4]

  """
  @spec merge(t, t, (key, value, value -> value)) :: t
  def merge(d1, d2, fun) when is_list(d1) and is_list(d2) do
    do_merge(d2, d1, fun)
  end

  defp do_merge([{k, v2}|t], acc, fun) do
    do_merge t, update(acc, k, v2, fn(v1) -> fun.(k, v1, v2) end), fun
  end

  defp do_merge([], acc, _fun) do
    acc
  end

  @doc """
  Returns whether a given `key` exists in the given `keywords`.

  ## Examples

      iex> Keyword.has_key?([a: 1], :a)
      true

      iex> Keyword.has_key?([a: 1], :b)
      false

  """
  @spec has_key?(t, key) :: boolean
  def has_key?(keywords, key) when is_list(keywords) and is_atom(key) do
    :lists.keymember(key, 1, keywords)
  end

  @doc """
  Updates the `key` with the given function.

  If the `key` does not exist, raises `KeyError`.

  If there are duplicated keys, they are all removed and only the first one
  is updated.

  ## Examples

      iex> Keyword.update!([a: 1], :a, &(&1 * 2))
      [a: 2]

      iex> Keyword.update!([a: 1], :b, &(&1 * 2))
      ** (KeyError) key :b not found in: [a: 1]

  """
  @spec update!(t, key, (value -> value)) :: t | no_return
  def update!(keywords, key, fun) do
    update!(keywords, key, fun, keywords)
  end

  defp update!([{key, value}|keywords], key, fun, _dict) do
    [{key, fun.(value)}|delete(keywords, key)]
  end

  defp update!([{_, _} = e|keywords], key, fun, dict) do
    [e|update!(keywords, key, fun, dict)]
  end

  defp update!([], key, _fun, dict) when is_atom(key) do
    raise(KeyError, key: key, term: dict)
  end

  @doc """
  Updates the `key` with the given function.

  If the `key` does not exist, inserts the given `initial` value.

  If there are duplicated keys, they are all removed and only the first one
  is updated.

  ## Examples

      iex> Keyword.update([a: 1], :a, 13, &(&1 * 2))
      [a: 2]

      iex> Keyword.update([a: 1], :b, 11, &(&1 * 2))
      [a: 1, b: 11]

  """
  @spec update(t, key, value, (value -> value)) :: t
  def update([{key, value}|keywords], key, _initial, fun) do
    [{key, fun.(value)}|delete(keywords, key)]
  end

  def update([{_, _} = e|keywords], key, initial, fun) do
    [e|update(keywords, key, initial, fun)]
  end

  def update([], key, initial, _fun) when is_atom(key) do
    [{key, initial}]
  end

  @doc """
  Takes all entries corresponding to the given keys and extracts them into a
  separate keyword list.

  Returns a tuple with the new list and the old list with removed keys.

  Keys for which there are no entires in the keyword list are ignored.

  Entries with duplicated keys end up in the same keyword list.

  ## Examples

      iex> d = [a: 1, b: 2, c: 3, d: 4]
      iex> Keyword.split(d, [:a, :c, :e])
      {[a: 1, c: 3], [b: 2, d: 4]}

      iex> d = [a: 1, b: 2, c: 3, d: 4, a: 5]
      iex> Keyword.split(d, [:a, :c, :e])
      {[a: 1, c: 3, a: 5], [b: 2, d: 4]}

  """
  def split(keywords, keys) when is_list(keywords) do
    fun = fn {k, v}, {take, drop} ->
      case k in keys do
        true  -> {[{k, v}|take], drop}
        false -> {take, [{k, v}|drop]}
      end
    end

    acc = {[], []}
    {take, drop} = :lists.foldl(fun, acc, keywords)
    {:lists.reverse(take), :lists.reverse(drop)}
  end

  @doc """
  Takes all entries corresponding to the given keys and returns them in a new
  keyword list.

  Duplicated keys are preserved in the new keyword list.

  ## Examples

      iex> d = [a: 1, b: 2, c: 3, d: 4]
      iex> Keyword.take(d, [:a, :c, :e])
      [a: 1, c: 3]

      iex> d = [a: 1, b: 2, c: 3, d: 4, a: 5]
      iex> Keyword.take(d, [:a, :c, :e])
      [a: 1, c: 3, a: 5]

  """
  def take(keywords, keys) when is_list(keywords) do
    :lists.filter(fn {k, _} -> k in keys end, keywords)
  end

  @doc """
  Drops the given keys from the keyword list.

  Duplicated keys are preserved in the new keyword list.

  ## Examples

      iex> d = [a: 1, b: 2, c: 3, d: 4]
      iex> Keyword.drop(d, [:b, :d])
      [a: 1, c: 3]

      iex> d = [a: 1, b: 2, b: 3, c: 3, d: 4, a: 5]
      iex> Keyword.drop(d, [:b, :d])
      [a: 1, c: 3, a: 5]

  """
  def drop(keywords, keys) when is_list(keywords) do
    :lists.filter(fn {k, _} -> not k in keys end, keywords)
  end

  @doc """
  Returns the first value associated with `key` in the keyword
  list as well as the keyword list without `key`.

  All duplicated keys are removed. See `pop_first/3` for
  removing only the first entry.

  ## Examples

      iex> Keyword.pop [a: 1], :a
      {1,[]}

      iex> Keyword.pop [a: 1], :b
      {nil,[a: 1]}

      iex> Keyword.pop [a: 1], :b, 3
      {3,[a: 1]}

      iex> Keyword.pop [a: 1], :b, 3
      {3,[a: 1]}

      iex> Keyword.pop [a: 1, a: 2], :a
      {1,[]}

  """
  @spec pop(t, key, value) :: {value, t}
  def pop(keywords, key, default \\ nil) when is_list(keywords) do
    {get(keywords, key, default), delete(keywords, key)}
  end

  @doc """
  Returns the first value associated with `key` in the keyword
  list as well as the keyword list without `key`.

  This is useful if the default value is very expensive to calculate or
  generally difficult to set-up and tear-down again.

  All duplicated keys are removed. See `pop_first/3` for
  removing only the first entry.

  ## Examples

      iex> keyword = [a: 1]
      iex> fun = fn ->
      ...>   # some expensive operation here
      ...>   :result
      ...> end
      iex> Keyword.pop_lazy(keyword, :a, fun)
      {1,[]}
      iex> Keyword.pop_lazy(keyword, :b, fun)
      {:result,[a: 1]}

  """
  @spec pop_lazy(t, key, (() -> value)) :: {value, t}
  def pop_lazy(keywords, key, fun) when is_list(keywords) do
    {get_lazy(keywords, key, fun), delete(keywords, key)}
  end

  @doc """
  Returns the first value associated with `key` in the keyword
  list as well as the keyword list without that particular occurrence
  of `key`.

  Duplicated keys are not removed.

  ## Examples

      iex> Keyword.pop_first [a: 1], :a
      {1,[]}

      iex> Keyword.pop_first [a: 1], :b
      {nil,[a: 1]}

      iex> Keyword.pop_first [a: 1], :b, 3
      {3,[a: 1]}

      iex> Keyword.pop_first [a: 1], :b, 3
      {3,[a: 1]}

      iex> Keyword.pop_first [a: 1, a: 2], :a
      {1,[a: 2]}

  """
  @spec pop_first(t, key, value) :: {value, t}
  def pop_first(keywords, key, default \\ nil) when is_list(keywords) do
    {get(keywords, key, default), delete_first(keywords, key)}
  end

  # Dict callbacks

  @doc false
  def size(keyword) do
    length(keyword)
  end

  @doc false
  def to_list(keyword) do
    keyword
  end
end

defmodule Keyword do
  @moduledoc """
  A keyword is a list of tuples where the first element
  of the tuple is an atom and the second element can be
  any value.

  A keyword may have duplicated keys so it is not strictly
  a dictionary. However most of the functions in this module
  behave exactly as a dictionary and mimic the API defined
  by the `Dict` behaviour.

  For example, `Keyword.get/3` will get the first entry matching
  the given key, regardless if duplicated entries exist.
  Similarly, `Keyword.put/3` and `Keyword.delete/3` ensure all
  duplicated entries for a given key are removed when invoked.

  A handful of functions exist to handle duplicated keys, in
  particular, `Enum.into/2` allows creating new keywords without
  removing duplicated keys, `get_values/2` returns all values for
  a given key and `delete_first/2` deletes just one of the existing
  entries.

  The functions in Keyword do not guarantee any property when
  it comes to ordering. However, since a keyword list is simply a
  list, all the operations defined in `Enum` and `List` can be
  applied too, specially when ordering is required.
  """

  @compile :inline_list_funcs
  @behaviour Dict

  @type key :: atom
  @type value :: any

  @type t :: [{key, value}]
  @type t(value) :: [{key, value}]

  @doc """
  Checks if the given argument is a keyword list or not.
  """
  @spec keyword?(term) :: boolean
  def keyword?([{key, _value} | rest]) when is_atom(key) do
    keyword?(rest)
  end

  def keyword?([]),     do: true
  def keyword?(_other), do: false

  @doc """
  Returns an empty keyword list, i.e. an empty list.
  """
  @spec new :: t
  def new do
    []
  end

  @doc """
  Creates a keyword from an enumerable.

  Duplicated entries are removed, the latest one prevails.
  Unlike `Enum.into(enumerable, [])`,
  `Keyword.new(enumerable)` guarantees the keys are unique.

  ## Examples

      iex> Keyword.new([{:b, 1}, {:a, 2}])
      [a: 2, b: 1]

  """
  @spec new(Enum.t) :: t
  def new(pairs) do
    Enum.reduce pairs, [], fn {k, v}, keywords ->
      put(keywords, k, v)
    end
  end

  @doc """
  Creates a keyword from an enumerable via the transformation function.

  Duplicated entries are removed, the latest one prevails.
  Unlike `Enum.into(enumerable, [], fun)`,
  `Keyword.new(enumerable, fun)` guarantees the keys are unique.

  ## Examples

      iex> Keyword.new([:a, :b], fn (x) -> {x, x} end) |> Enum.sort
      [a: :a, b: :b]

  """
  @spec new(Enum.t, ({key, value} -> {key, value})) :: t
  def new(pairs, transform) do
    Enum.reduce pairs, [], fn i, keywords ->
      {k, v} = transform.(i)
      put(keywords, k, v)
    end
  end

  @doc """
  Gets the value for a specific `key`.

  If `key` does not exist, return the default value (`nil` if no default value).

  If duplicated entries exist, the first one is returned.
  Use `get_values/2` to retrieve all entries.

  ## Examples

      iex> Keyword.get([a: 1], :a)
      1

      iex> Keyword.get([a: 1], :b)
      nil

      iex> Keyword.get([a: 1], :b, 3)
      3

  """
  @spec get(t, key) :: value
  @spec get(t, key, value) :: value
  def get(keywords, key, default \\ nil) when is_list(keywords) and is_atom(key) do
    case :lists.keyfind(key, 1, keywords) do
      {^key, value} -> value
      false -> default
    end
  end

  @doc """
  Gets the value for a specific `key`.

  If `key` does not exist, lazily evaluates `fun` and returns its result.

  This is useful if the default value is very expensive to calculate or
  generally difficult to set-up and tear-down again.

  If duplicated entries exist, the first one is returned.
  Use `get_values/2` to retrieve all entries.

  ## Examples

      iex> keyword = [a: 1]
      iex> fun = fn ->
      ...>   # some expensive operation here
      ...>   :result
      ...> end
      iex> Keyword.get_lazy(keyword, :a, fun)
      1
      iex> Keyword.get_lazy(keyword, :b, fun)
      :result

  """
  @spec get_lazy(t, key, (() -> value)) :: value
  def get_lazy(keywords, key, fun)
      when is_list(keywords) and is_atom(key) and is_function(fun, 0) do
    case :lists.keyfind(key, 1, keywords) do
      {^key, value} -> value
      false -> fun.()
    end
  end

  @doc """
  Gets the value from `key` and updates it, all in one pass.

  This `fun` argument receives the value of `key` (or `nil` if `key`
  is not present) and must return a two-elements 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 returned value is a tuple with the "get" value returned by `fun` and a new
  keyword list with the updated value under `key`.

  ## Examples

      iex> Keyword.get_and_update [a: 1], :a, fn(current_value) ->
      ...>   {current_value, "new value!"}
      ...> end
      {1, [a: "new value!"]}

  """
  @spec get_and_update(t, key, (value -> {value, value})) :: {value, t}
  def get_and_update(keywords, key, fun)
    when is_list(keywords) and is_atom(key),
    do: get_and_update(keywords, [], key, fun)

  defp get_and_update([{key, value}|t], acc, key, fun) do
    {get, new_value} = fun.(value)
    {get, :lists.reverse(acc, [{key, new_value}|t])}
  end

  defp get_and_update([h|t], acc, key, fun) do
    get_and_update(t, [h|acc], key, fun)
  end

  defp get_and_update([], acc, key, fun) do
    {get, update} = fun.(nil)
    {get, [{key, update}|List.reverse(acc)]}
  end

  @doc """
  Fetches the value for a specific `key` and returns it in a tuple.

  If the `key` does not exist, returns `:error`.

  ## Examples

      iex> Keyword.fetch([a: 1], :a)
      {:ok, 1}

      iex> Keyword.fetch([a: 1], :b)
      :error

  """
  @spec fetch(t, key) :: {:ok, value} | :error
  def fetch(keywords, key) when is_list(keywords) and is_atom(key) do
    case :lists.keyfind(key, 1, keywords) do
      {^key, value} -> {:ok, value}
      false -> :error
    end
  end

  @doc """
  Fetches the value for specific `key`.

  If `key` does not exist, a `KeyError` is raised.

  ## Examples

      iex> Keyword.fetch!([a: 1], :a)
      1

      iex> Keyword.fetch!([a: 1], :b)
      ** (KeyError) key :b not found in: [a: 1]

  """
  @spec fetch!(t, key) :: value | no_return
  def fetch!(keywords, key) when is_list(keywords) and is_atom(key) do
    case :lists.keyfind(key, 1, keywords) do
      {^key, value} -> value
      false -> raise(KeyError, key: key, term: keywords)
    end
  end

  @doc """
  Gets all values for a specific `key`.

  ## Examples

      iex> Keyword.get_values([a: 1, a: 2], :a)
      [1,2]

  """
  @spec get_values(t, key) :: [value]
  def get_values(keywords, key) when is_list(keywords) and is_atom(key) do
    fun = fn
      {k, v} when k === key -> {true, v}
      {_, _} -> false
    end

    :lists.filtermap(fun, keywords)
  end

  @doc """
  Returns all keys from the keyword list.

  Duplicated keys appear duplicated in the final list of keys.

  ## Examples

      iex> Keyword.keys([a: 1, b: 2])
      [:a,:b]

      iex> Keyword.keys([a: 1, b: 2, a: 3])
      [:a,:b,:a]

  """
  @spec keys(t) :: [key]
  def keys(keywords) when is_list(keywords) do
    :lists.map(fn {k, _} -> k end, keywords)
  end

  @doc """
  Returns all values from the keyword list.

  ## Examples

      iex> Keyword.values([a: 1, b: 2])
      [1,2]

  """
  @spec values(t) :: [value]
  def values(keywords) when is_list(keywords) do
    :lists.map(fn {_, v} -> v end, keywords)
  end

  @doc """
  Deletes the entries in the keyword list for a `key` with `value`.

  If no `key` with `value` exists, returns the keyword list unchanged.

  ## Examples

      iex> Keyword.delete([a: 1, b: 2], :a, 1)
      [b: 2]

      iex> Keyword.delete([a: 1, b: 2, a: 3], :a, 3)
      [a: 1, b: 2]

      iex> Keyword.delete([b: 2], :a, 5)
      [b: 2]

  """
  @spec delete(t, key, value) :: t
  def delete(keywords, key, value) when is_list(keywords) and is_atom(key) do
    :lists.filter(fn {k, v} -> k != key or v != value end, keywords)
  end

  @doc """
  Deletes the entries in the keyword list for a specific `key`.

  If the `key` does not exist, returns the keyword list unchanged.
  Use `delete_first/2` to delete just the first entry in case of
  duplicated keys.

  ## Examples

      iex> Keyword.delete([a: 1, b: 2], :a)
      [b: 2]

      iex> Keyword.delete([a: 1, b: 2, a: 3], :a)
      [b: 2]

      iex> Keyword.delete([b: 2], :a)
      [b: 2]

  """
  @spec delete(t, key) :: t
  def delete(keywords, key) when is_list(keywords) and is_atom(key) do
    :lists.filter(fn {k, _} -> k != key end, keywords)
  end

  @doc """
  Deletes the first entry in the keyword list for a specific `key`.

  If the `key` does not exist, returns the keyword list unchanged.

  ## Examples

      iex> Keyword.delete_first([a: 1, b: 2, a: 3], :a)
      [b: 2, a: 3]

      iex> Keyword.delete_first([b: 2], :a)
      [b: 2]

  """
  @spec delete_first(t, key) :: t
  def delete_first(keywords, key) when is_list(keywords) and is_atom(key) do
    :lists.keydelete(key, 1, keywords)
  end

  @doc """
  Puts the given `value` under `key`.

  If a previous value is already stored, all entries are
  removed and the value is overridden.

  ## Examples

      iex> Keyword.put([a: 1, b: 2], :a, 3)
      [a: 3, b: 2]

      iex> Keyword.put([a: 1, b: 2, a: 4], :a, 3)
      [a: 3, b: 2]

  """
  @spec put(t, key, value) :: t
  def put(keywords, key, value) when is_list(keywords) and is_atom(key) do
    [{key, value}|delete(keywords, key)]
  end

  @doc """
  Evaluates `fun` and puts the result under `key`
  in keyword list unless `key` is already present.

  This is useful if the value is very expensive to calculate or generally
  difficult to set-up and tear-down again.

  ## Examples

      iex> keyword = [a: 1]
      iex> fun = fn ->
      ...>   # some expensive operation here
      ...>   3
      ...> end
      iex> Keyword.put_new_lazy(keyword, :a, fun)
      [a: 1]
      iex> Keyword.put_new_lazy(keyword, :b, fun)
      [b: 3, a: 1]

  """
  @spec put_new_lazy(t, key, (() -> value)) :: t
  def put_new_lazy(keywords, key, fun)
      when is_list(keywords) and is_atom(key) and is_function(fun, 0) do
    case :lists.keyfind(key, 1, keywords) do
      {^key, _} -> keywords
      false -> [{key, fun.()}|keywords]
    end
  end

  @doc """
  Puts the given `value` under `key` unless the entry `key`
  already exists.

  ## Examples

      iex> Keyword.put_new([a: 1], :b, 2)
      [b: 2, a: 1]

      iex> Keyword.put_new([a: 1, b: 2], :a, 3)
      [a: 1, b: 2]

  """
  @spec put_new(t, key, value) :: t
  def put_new(keywords, key, value) when is_list(keywords) and is_atom(key) do
    case :lists.keyfind(key, 1, keywords) do
      {^key, _} -> keywords
      false -> [{key, value}|keywords]
    end
  end

  @doc """
  Checks if two keywords are equal.

  Two keywords are considered to be equal if they contain
  the same keys and those keys contain the same values.

  ## Examples

      iex> Keyword.equal?([a: 1, b: 2], [b: 2, a: 1])
      true

  """
  @spec equal?(t, t) :: boolean
  def equal?(left, right) when is_list(left) and is_list(right) do
    :lists.sort(left) == :lists.sort(right)
  end

  @doc """
  Merges two keyword lists into one.

  If they have duplicated keys, the one given in the second argument wins.

  ## Examples

      iex> Keyword.merge([a: 1, b: 2], [a: 3, d: 4])
      [a: 3, d: 4, b: 2]

  """
  @spec merge(t, t) :: t
  def merge(d1, d2) when is_list(d1) and is_list(d2) do
    fun = fn {k, _v} -> not has_key?(d2, k) end
    d2 ++ :lists.filter(fun, d1)
  end

  @doc """
  Merges two keyword lists into one.

  If they have duplicated keys, the given function is invoked to solve conflicts.

  ## Examples

      iex> Keyword.merge([a: 1, b: 2], [a: 3, d: 4], fn (_k, v1, v2) ->
      ...>  v1 + v2
      ...> end)
      [a: 4, b: 2, d: 4]

  """
  @spec merge(t, t, (key, value, value -> value)) :: t
  def merge(d1, d2, fun) when is_list(d1) and is_list(d2) do
    do_merge(d2, d1, fun)
  end

  defp do_merge([{k, v2}|t], acc, fun) do
    do_merge t, update(acc, k, v2, fn(v1) -> fun.(k, v1, v2) end), fun
  end

  defp do_merge([], acc, _fun) do
    acc
  end

  @doc """
  Returns whether a given `key` exists in the given `keywords`.

  ## Examples

      iex> Keyword.has_key?([a: 1], :a)
      true

      iex> Keyword.has_key?([a: 1], :b)
      false

  """
  @spec has_key?(t, key) :: boolean
  def has_key?(keywords, key) when is_list(keywords) and is_atom(key) do
    :lists.keymember(key, 1, keywords)
  end

  @doc """
  Updates the `key` with the given function.

  If the `key` does not exist, raises `KeyError`.

  If there are duplicated keys, they are all removed and only the first one
  is updated.

  ## Examples

      iex> Keyword.update!([a: 1], :a, &(&1 * 2))
      [a: 2]

      iex> Keyword.update!([a: 1], :b, &(&1 * 2))
      ** (KeyError) key :b not found in: [a: 1]

  """
  @spec update!(t, key, (value -> value)) :: t | no_return
  def update!(keywords, key, fun) do
    update!(keywords, key, fun, keywords)
  end

  defp update!([{key, value}|keywords], key, fun, _dict) do
    [{key, fun.(value)}|delete(keywords, key)]
  end

  defp update!([{_, _} = e|keywords], key, fun, dict) do
    [e|update!(keywords, key, fun, dict)]
  end

  defp update!([], key, _fun, dict) when is_atom(key) do
    raise(KeyError, key: key, term: dict)
  end

  @doc """
  Updates the `key` with the given function.

  If the `key` does not exist, inserts the given `initial` value.

  If there are duplicated keys, they are all removed and only the first one
  is updated.

  ## Examples

      iex> Keyword.update([a: 1], :a, 13, &(&1 * 2))
      [a: 2]

      iex> Keyword.update([a: 1], :b, 11, &(&1 * 2))
      [a: 1, b: 11]

  """
  @spec update(t, key, value, (value -> value)) :: t
  def update([{key, value}|keywords], key, _initial, fun) do
    [{key, fun.(value)}|delete(keywords, key)]
  end

  def update([{_, _} = e|keywords], key, initial, fun) do
    [e|update(keywords, key, initial, fun)]
  end

  def update([], key, initial, _fun) when is_atom(key) do
    [{key, initial}]
  end

  @doc """
  Takes all entries corresponding to the given keys and extracts them into a
  separate keyword list.

  Returns a tuple with the new list and the old list with removed keys.

  Keys for which there are no entires in the keyword list are ignored.

  Entries with duplicated keys end up in the same keyword list.

  ## Examples

      iex> d = [a: 1, b: 2, c: 3, d: 4]
      iex> Keyword.split(d, [:a, :c, :e])
      {[a: 1, c: 3], [b: 2, d: 4]}

      iex> d = [a: 1, b: 2, c: 3, d: 4, a: 5]
      iex> Keyword.split(d, [:a, :c, :e])
      {[a: 1, c: 3, a: 5], [b: 2, d: 4]}

  """
  def split(keywords, keys) when is_list(keywords) do
    fun = fn {k, v}, {take, drop} ->
      case k in keys do
        true  -> {[{k, v}|take], drop}
        false -> {take, [{k, v}|drop]}
      end
    end

    acc = {[], []}
    {take, drop} = :lists.foldl(fun, acc, keywords)
    {:lists.reverse(take), :lists.reverse(drop)}
  end

  @doc """
  Takes all entries corresponding to the given keys and returns them in a new
  keyword list.

  Duplicated keys are preserved in the new keyword list.

  ## Examples

      iex> d = [a: 1, b: 2, c: 3, d: 4]
      iex> Keyword.take(d, [:a, :c, :e])
      [a: 1, c: 3]

      iex> d = [a: 1, b: 2, c: 3, d: 4, a: 5]
      iex> Keyword.take(d, [:a, :c, :e])
      [a: 1, c: 3, a: 5]

  """
  def take(keywords, keys) when is_list(keywords) do
    :lists.filter(fn {k, _} -> k in keys end, keywords)
  end

  @doc """
  Drops the given keys from the keyword list.

  Duplicated keys are preserved in the new keyword list.

  ## Examples

      iex> d = [a: 1, b: 2, c: 3, d: 4]
      iex> Keyword.drop(d, [:b, :d])
      [a: 1, c: 3]

      iex> d = [a: 1, b: 2, b: 3, c: 3, d: 4, a: 5]
      iex> Keyword.drop(d, [:b, :d])
      [a: 1, c: 3, a: 5]

  """
  def drop(keywords, keys) when is_list(keywords) do
    :lists.filter(fn {k, _} -> not k in keys end, keywords)
  end

  @doc """
  Returns the first value associated with `key` in the keyword
  list as well as the keyword list without `key`.

  All duplicated keys are removed. See `pop_first/3` for
  removing only the first entry.

  ## Examples

      iex> Keyword.pop [a: 1], :a
      {1,[]}

      iex> Keyword.pop [a: 1], :b
      {nil,[a: 1]}

      iex> Keyword.pop [a: 1], :b, 3
      {3,[a: 1]}

      iex> Keyword.pop [a: 1], :b, 3
      {3,[a: 1]}

      iex> Keyword.pop [a: 1, a: 2], :a
      {1,[]}

  """
  def pop(keywords, key, default \\ nil) when is_list(keywords) do
    {get(keywords, key, default), delete(keywords, key)}
  end

  @doc """
  Returns the first value associated with `key` in the keyword
  list as well as the keyword list without `key`.

  This is useful if the default value is very expensive to calculate or
  generally difficult to set-up and tear-down again.

  All duplicated keys are removed. See `pop_first/3` for
  removing only the first entry.

  ## Examples

      iex> keyword = [a: 1]
      iex> fun = fn ->
      ...>   # some expensive operation here
      ...>   :result
      ...> end
      iex> Keyword.pop_lazy(keyword, :a, fun)
      {1,[]}
      iex> Keyword.pop_lazy(keyword, :b, fun)
      {:result,[a: 1]}

  """
  @spec pop_lazy(t, key, (() -> value)) :: {value, t}
  def pop_lazy(keywords, key, fun) when is_list(keywords) do
    {get_lazy(keywords, key, fun), delete(keywords, key)}
  end

  @doc """
  Returns the first value associated with `key` in the keyword
  list as well as the keyword list without that particular occurrence
  of `key`.

  Duplicated keys are not removed.

  ## Examples

      iex> Keyword.pop_first [a: 1], :a
      {1,[]}

      iex> Keyword.pop_first [a: 1], :b
      {nil,[a: 1]}

      iex> Keyword.pop_first [a: 1], :b, 3
      {3,[a: 1]}

      iex> Keyword.pop_first [a: 1], :b, 3
      {3,[a: 1]}

      iex> Keyword.pop_first [a: 1, a: 2], :a
      {1,[a: 2]}

  """
  def pop_first(keywords, key, default \\ nil) when is_list(keywords) do
    {get(keywords, key, default), delete_first(keywords, key)}
  end

  # Dict callbacks

  @doc false
  def size(keyword) do
    length(keyword)
  end

  @doc false
  def to_list(keyword) do
    keyword
  end
end


Add missing comments about inlining in Process

defmodule Process do
  @moduledoc """
  Conveniences for working with processes and the process dictionary.

  Besides the functions available in this module, the `Kernel` module
  exposes and auto-imports some basic functionality related to processes
  available through the functions:

    * `Kernel.spawn/1` and `Kernel.spawn/3`
    * `Kernel.spawn_link/1` and `Kernel.spawn_link/3`
    * `Kernel.spawn_monitor/1` and `Kernel.spawn_monitor/3`
    * `Kernel.self/0`
    * `Kernel.send/2`

  """

  @doc """
  Returns `true` if the process exists and is alive (i.e. it is not exiting
  and has not exited yet). Otherwise, returns `false`.

  `pid` must refer to a process at the local node.

  Inlined by the compiler.
  """
  @spec alive?(pid) :: boolean
  def alive?(pid) do
    :erlang.is_process_alive(pid)
  end

  @doc """
  Returns all key-values in the dictionary.

  Inlined by the compiler.
  """
  @spec get :: [{term, term}]
  def get do
    :erlang.get()
  end

  @doc """
  Returns the value for the given `key`.
  """
  @spec get(term) :: term
  @spec get(term, default :: term) :: term
  def get(key, default \\ nil) do
    case :erlang.get(key) do
      :undefined ->
        default
      value ->
        value
    end
  end

  @doc """
  Returns all keys in the process dictionary.

  Inlined by the compiler.
  """
  @spec get_keys() :: [term]
  def get_keys() do
    :erlang.get_keys()
  end

  @doc """
  Returns all keys that have the given `value`.

  Inlined by the compiler.
  """
  @spec get_keys(term) :: [term]
  def get_keys(value) do
    :erlang.get_keys(value)
  end

  @doc """
  Stores the given key-value in the process dictionary.

  The return value is the value that was previously stored under the key `key`
  (or `nil` in case no value was stored under `key`).
  """
  @spec put(term, term) :: term | nil
  def put(key, value) do
    nillify :erlang.put(key, value)
  end

  @doc """
  Deletes the given `key` from the dictionary.
  """
  @spec delete(term) :: term | nil
  def delete(key) do
    nillify :erlang.erase(key)
  end

  @doc """
  Sends an exit signal with the given reason to the pid.

  The following behaviour applies if reason is any term except `:normal` or `:kill`:

    1. If pid is not trapping exits, pid will exit with the given reason.

    2. If pid is trapping exits, the exit signal is transformed into a message
       `{:EXIT, from, reason}` and delivered to the message queue of pid.

    3. If reason is the atom `:normal`, pid will not exit (unless it is the calling
       process's pid, in which case it will exit with the reason `:normal`).
       If it is trapping exits, the exit signal is transformed into a message
       `{:EXIT, from, :normal}` and delivered to its message queue.

    4. If reason is the atom `:kill`, that is if `exit(pid, :kill)` is called,
       an untrappable exit signal is sent to pid which will unconditionally
       exit with exit reason `:killed`.

  Inlined by the compiler.

  ## Examples

      Process.exit(pid, :kill)

  """
  @spec exit(pid, term) :: true
  def exit(pid, reason) do
    :erlang.exit(pid, reason)
  end

  @doc """
  Sends a message to the given process.

  If the option `:noconnect` is used and sending the message would require an
  auto-connection to another node the message is not sent and `:noconnect` is
  returned.

  If the option `:nosuspend` is used and sending the message would cause the
  sender to be suspended the message is not sent and `:nosuspend` is returned.

  Otherwise the message is sent and `:ok` is returned.

  ## Examples

      iex> Process.send({:name, :node_does_not_exist}, :hi, [:noconnect])
      :noconnect

  """
  @spec send(dest, msg, [option]) :: :ok | :noconnect | :nosuspend when
        dest: pid | port | atom | {atom, node},
        msg: any,
        option: :noconnect | :nosuspend
  def send(dest, msg, options) do
    :erlang.send(dest, msg, options)
  end

  @doc """
  Sends `msg` to `dest` after `time` milliseconds.

  If `dest` is a pid, it must be the pid of a local process, dead or alive.
  If `dest` is an atom, it must be the name of a registered process
  which is looked up at the time of delivery. No error is given if the name does
  not refer to a process.

  This function returns a timer reference, which can be read or canceled with
  `read_timer/1` and `cancel_timer/1`.

  Finally, the timer will be automatically canceled if the given `dest` is a pid
  which is not alive or when the given pid exits. Note that timers will not be
  automatically canceled when `dest` is an atom (as the atom resolution is done
  on delivery).
  """
  @spec send_after(pid | atom, term, non_neg_integer) :: reference
  def send_after(dest, msg, time) do
    :erlang.send_after(time, dest, msg)
  end

  @doc """
  Cancels a timer created by `send_after/3`.

  When the result is an integer, it represents the time in milli-seconds
  left until the timer will expire.

  When the result is `false`, a timer corresponding to `timer_ref` could
  not be found. This can be either because the timer expired, already has
  been canceled, or because `timer_ref` never corresponded to a timer.

  If the timer has expired, the timeout message has been sent, but it does
  not tell you whether or not it has arrived at its destination yet.

  Inlined by the compiler.
  """
  @spec cancel_timer(reference) :: non_neg_integer | false
  def cancel_timer(timer_ref) do
    :erlang.cancel_timer(timer_ref)
  end

  @doc """
  Reads a timer created by `send_after/3`.

  When the result is an integer, it represents the time in milli-seconds
  left until the timer will expire.

  When the result is `false`, a timer corresponding to `timer_ref` could
  not be found. This can be either because the timer expired, already has
  been canceled, or because `timer_ref` never corresponded to a timer.

  If the timer has expired, the timeout message has been sent, but it does
  not tell you whether or not it has arrived at its destination yet.

  Inlined by the compiler.
  """
  @spec read_timer(reference) :: non_neg_integer | false
  def read_timer(timer_ref) do
    :erlang.read_timer(timer_ref)
  end

  @type spawn_opt  :: :link | :monitor | {:priority, :low | :normal | :high} |
                      {:fullsweep_after, non_neg_integer} |
                      {:min_heap_size, non_neg_integer} |
                      {:min_bin_vheap_size, non_neg_integer}
  @type spawn_opts :: [spawn_opt]

  @doc """
  Spawns the given module and function passing the given args
  according to the given options.

  The result depends on the given options. In particular,
  if `:monitor` is given as an option, it will return a tuple
  containing the pid and the monitoring reference, otherwise
  just the spawned process pid.

  It also accepts extra options, for the list of available options
  check [`:erlang.spawn_opt/4`](http://www.erlang.org/doc/man/erlang.html#spawn_opt-4).

  Inlined by the compiler.
  """
  @spec spawn((() -> any), spawn_opts) :: pid | {pid, reference}
  def spawn(fun, opts) do
    :erlang.spawn_opt(fun, opts)
  end

  @doc """
  Spawns the given module and function passing the given args
  according to the given options.

  The result depends on the given options. In particular,
  if `:monitor` is given as an option, it will return a tuple
  containing the pid and the monitoring reference, otherwise
  just the spawned process pid.

  It also accepts extra options, for the list of available options
  check [`:erlang.spawn_opt/4`](http://www.erlang.org/doc/man/erlang.html#spawn_opt-4).

  Inlined by the compiler.
  """
  @spec spawn(module, atom, list, spawn_opts) :: pid | {pid, reference}
  def spawn(mod, fun, args, opts) do
    :erlang.spawn_opt(mod, fun, args, opts)
  end

  @doc """
  The calling process starts monitoring the item given.
  It returns the monitor reference.

  See [the need for monitoring](http://elixir-lang.org/getting-started/mix-otp/genserver.html#the-need-for-monitoring)
  for an example.
  See [`:erlang.monitor/2`](http://www.erlang.org/doc/man/erlang.html#monitor-2) for more info.

  Inlined by the compiler.
  """
  @spec monitor(pid | {reg_name :: atom, node :: atom} | reg_name :: atom) :: reference
  def monitor(item) do
    :erlang.monitor(:process, item)
  end

  @doc """
  If `monitor_ref` is a reference which the calling process
  obtained by calling `monitor/1`, this monitoring is turned off.
  If the monitoring is already turned off, nothing happens.

  See [`:erlang.demonitor/2`](http://www.erlang.org/doc/man/erlang.html#demonitor-2) for more info.

  Inlined by the compiler.
  """
  @spec demonitor(reference) :: true
  @spec demonitor(reference, options :: [:flush | :info]) :: boolean
  def demonitor(monitor_ref, options \\ []) do
    :erlang.demonitor(monitor_ref, options)
  end

  @doc """
  Returns a list of process identifiers corresponding to all the
  processes currently existing on the local node.

  Note that a process that is exiting, exists but is not alive, i.e.,
  `alive?/1` will return `false` for a process that is exiting,
  but its process identifier will be part of the result returned.

  See [`:erlang.processes/0`](http://www.erlang.org/doc/man/erlang.html#processes-0) for more info.
  """
  @spec list :: [pid]
  def list do
    :erlang.processes()
  end

  @doc """
  Creates a link between the calling process and another process
  (or port) `pid`, if there is not such a link already.

  See [`:erlang.link/1`](http://www.erlang.org/doc/man/erlang.html#link-1) for more info.

  Inlined by the compiler.
  """
  @spec link(pid | port) :: true
  def link(pid) do
    :erlang.link(pid)
  end

  @doc """
  Removes the link, if there is one, between the calling process and
  the process or port referred to by `pid`. Returns `true` and does not
  fail, even if there is no link or `id` does not exist

  See [`:erlang.unlink/1`](http://www.erlang.org/doc/man/erlang.html#unlink-1) for more info.

  Inlined by the compiler.
  """
  @spec unlink(pid | port) :: true
  def unlink(pid) do
    :erlang.unlink(pid)
  end

  @doc """
  Associates the name with a pid or a port identifier. `name`, which must
  be an atom, can be used instead of the pid / port identifier with the
  `Kernel.send/2` function.

  `Process.register/2` will fail with `ArgumentError` if the pid supplied
  is no longer alive, (check with `alive?/1`) or if the name is
  already registered (check with `whereis/1`).
  """
  @spec register(pid | port, atom) :: true
  def register(pid, name) when not name in [nil, false, true] do
    :erlang.register(name, pid)
  end

  @doc """
  Removes the registered name, associated with a pid or a port identifier.

  See [`:erlang.unregister/1`](http://www.erlang.org/doc/man/erlang.html#unregister-1) for more info.
  """
  @spec unregister(atom) :: true
  def unregister(name) do
    :erlang.unregister(name)
  end

  @doc """
  Returns the pid or port identifier with the registered name.
  Returns `nil` if the name is not registered.

  See [`:erlang.whereis/1`](http://www.erlang.org/doc/man/erlang.html#whereis-1) for more info.
  """
  @spec whereis(atom) :: pid | port | nil
  def whereis(name) do
    nillify :erlang.whereis(name)
  end

  @doc """
  Returns the pid of the group leader for the process which evaluates the function.
  """
  @spec group_leader :: pid
  def group_leader do
    :erlang.group_leader
  end

  @doc """
  Sets the group leader of `pid` to `leader`. Typically, this is used when a processes
  started from a certain shell should have a group leader other than `:init`.
  """
  @spec group_leader(pid, leader :: pid) :: true
  def group_leader(pid, leader) do
    :erlang.group_leader(leader, pid)
  end

  @doc """
  Returns a list of names which have been registered using `register/2`.
  """
  @spec registered :: [atom]
  def registered do
    :erlang.registered()
  end

  @typep process_flag :: :trap_exit | :error_handler | :min_heap_size |
                         :min_bin_vheap_size | :priority | :save_calls |
                         :sensitive
  @doc """
  Sets certain flags for the process which calls this function.
  Returns the old value of the flag.

  See [`:erlang.process_flag/2`](http://www.erlang.org/doc/man/erlang.html#process_flag-2) for more info.
  """
  @spec flag(process_flag, term) :: term
  def flag(flag, value) do
    :erlang.process_flag(flag, value)
  end

  @doc """
  Sets certain flags for the process `pid`, in the same manner as `flag/2`.
  Returns the old value of the flag. The allowed values for `flag` are
  only a subset of those allowed in `flag/2`, namely: `save_calls`.

  See [`:erlang.process_flag/3`](http://www.erlang.org/doc/man/erlang.html#process_flag-3) for more info.
  """
  @spec flag(pid, :save_calls, non_neg_integer) :: non_neg_integer
  def flag(pid, flag, value) do
    :erlang.process_flag(pid, flag, value)
  end

  @doc """
  Returns information about the process identified by `pid` or `nil` if the process
  is not alive.
  Use this only for debugging information.

  See [`:erlang.process_info/1`](http://www.erlang.org/doc/man/erlang.html#process_info-1) for more info.
  """
  @spec info(pid) :: Keyword.t
  def info(pid) do
    nillify :erlang.process_info(pid)
  end

  @doc """
  Returns information about the process identified by `pid`
  or `nil` if the process is not alive.

  See [`:erlang.process_info/2`](http://www.erlang.org/doc/man/erlang.html#process_info-2) for more info.
  """
  @spec info(pid, atom | [atom]) :: {atom, term} | [{atom, term}]  | nil
  def info(pid, spec)

  def info(pid, :registered_name) do
    case :erlang.process_info(pid, :registered_name) do
      :undefined -> nil
      [] -> {:registered_name, []}
      other -> other
    end
  end

  def info(pid, spec) when is_atom(spec) or is_list(spec) do
    nillify :erlang.process_info(pid, spec)
  end

  @doc """
  Puts the calling process into a wait state
  where its memory allocation has been reduced as much as possible,
  which is useful if the process does not expect to receive any messages
  in the near future.

  See [`:erlang.hibernate/3`](http://www.erlang.org/doc/man/erlang.html#hibernate-3) for more info.

  Inlined by the compiler.
  """
  @spec hibernate(module, atom, list) :: no_return
  def hibernate(mod, fun, args) do
    :erlang.hibernate(mod, fun, args)
  end

  @compile {:inline, nillify: 1}
  defp nillify(:undefined), do: nil
  defp nillify(other),      do: other
end

defmodule Process do
  @moduledoc """
  Conveniences for working with processes and the process dictionary.

  Besides the functions available in this module, the `Kernel` module
  exposes and auto-imports some basic functionality related to processes
  available through the functions:

    * `Kernel.spawn/1` and `Kernel.spawn/3`
    * `Kernel.spawn_link/1` and `Kernel.spawn_link/3`
    * `Kernel.spawn_monitor/1` and `Kernel.spawn_monitor/3`
    * `Kernel.self/0`
    * `Kernel.send/2`

  """

  @doc """
  Returns `true` if the process exists and is alive (i.e. it is not exiting
  and has not exited yet). Otherwise, returns `false`.

  `pid` must refer to a process at the local node.

  Inlined by the compiler.
  """
  @spec alive?(pid) :: boolean
  def alive?(pid) do
    :erlang.is_process_alive(pid)
  end

  @doc """
  Returns all key-values in the dictionary.

  Inlined by the compiler.
  """
  @spec get :: [{term, term}]
  def get do
    :erlang.get()
  end

  @doc """
  Returns the value for the given `key`.
  """
  @spec get(term) :: term
  @spec get(term, default :: term) :: term
  def get(key, default \\ nil) do
    case :erlang.get(key) do
      :undefined ->
        default
      value ->
        value
    end
  end

  @doc """
  Returns all keys in the process dictionary.

  Inlined by the compiler.
  """
  @spec get_keys() :: [term]
  def get_keys() do
    :erlang.get_keys()
  end

  @doc """
  Returns all keys that have the given `value`.

  Inlined by the compiler.
  """
  @spec get_keys(term) :: [term]
  def get_keys(value) do
    :erlang.get_keys(value)
  end

  @doc """
  Stores the given key-value in the process dictionary.

  The return value is the value that was previously stored under the key `key`
  (or `nil` in case no value was stored under `key`).
  """
  @spec put(term, term) :: term | nil
  def put(key, value) do
    nillify :erlang.put(key, value)
  end

  @doc """
  Deletes the given `key` from the dictionary.
  """
  @spec delete(term) :: term | nil
  def delete(key) do
    nillify :erlang.erase(key)
  end

  @doc """
  Sends an exit signal with the given reason to the pid.

  The following behaviour applies if reason is any term except `:normal` or `:kill`:

    1. If pid is not trapping exits, pid will exit with the given reason.

    2. If pid is trapping exits, the exit signal is transformed into a message
       `{:EXIT, from, reason}` and delivered to the message queue of pid.

    3. If reason is the atom `:normal`, pid will not exit (unless it is the calling
       process's pid, in which case it will exit with the reason `:normal`).
       If it is trapping exits, the exit signal is transformed into a message
       `{:EXIT, from, :normal}` and delivered to its message queue.

    4. If reason is the atom `:kill`, that is if `exit(pid, :kill)` is called,
       an untrappable exit signal is sent to pid which will unconditionally
       exit with exit reason `:killed`.

  Inlined by the compiler.

  ## Examples

      Process.exit(pid, :kill)

  """
  @spec exit(pid, term) :: true
  def exit(pid, reason) do
    :erlang.exit(pid, reason)
  end

  @doc """
  Sends a message to the given process.

  If the option `:noconnect` is used and sending the message would require an
  auto-connection to another node the message is not sent and `:noconnect` is
  returned.

  If the option `:nosuspend` is used and sending the message would cause the
  sender to be suspended the message is not sent and `:nosuspend` is returned.

  Otherwise the message is sent and `:ok` is returned.

  ## Examples

      iex> Process.send({:name, :node_does_not_exist}, :hi, [:noconnect])
      :noconnect

  """
  @spec send(dest, msg, [option]) :: :ok | :noconnect | :nosuspend when
        dest: pid | port | atom | {atom, node},
        msg: any,
        option: :noconnect | :nosuspend
  def send(dest, msg, options) do
    :erlang.send(dest, msg, options)
  end

  @doc """
  Sends `msg` to `dest` after `time` milliseconds.

  If `dest` is a pid, it must be the pid of a local process, dead or alive.
  If `dest` is an atom, it must be the name of a registered process
  which is looked up at the time of delivery. No error is given if the name does
  not refer to a process.

  This function returns a timer reference, which can be read or canceled with
  `read_timer/1` and `cancel_timer/1`.

  Finally, the timer will be automatically canceled if the given `dest` is a pid
  which is not alive or when the given pid exits. Note that timers will not be
  automatically canceled when `dest` is an atom (as the atom resolution is done
  on delivery).
  """
  @spec send_after(pid | atom, term, non_neg_integer) :: reference
  def send_after(dest, msg, time) do
    :erlang.send_after(time, dest, msg)
  end

  @doc """
  Cancels a timer created by `send_after/3`.

  When the result is an integer, it represents the time in milli-seconds
  left until the timer will expire.

  When the result is `false`, a timer corresponding to `timer_ref` could
  not be found. This can be either because the timer expired, already has
  been canceled, or because `timer_ref` never corresponded to a timer.

  If the timer has expired, the timeout message has been sent, but it does
  not tell you whether or not it has arrived at its destination yet.
  """
  @spec cancel_timer(reference) :: non_neg_integer | false
  def cancel_timer(timer_ref) do
    :erlang.cancel_timer(timer_ref)
  end

  @doc """
  Reads a timer created by `send_after/3`.

  When the result is an integer, it represents the time in milli-seconds
  left until the timer will expire.

  When the result is `false`, a timer corresponding to `timer_ref` could
  not be found. This can be either because the timer expired, already has
  been canceled, or because `timer_ref` never corresponded to a timer.

  If the timer has expired, the timeout message has been sent, but it does
  not tell you whether or not it has arrived at its destination yet.
  """
  @spec read_timer(reference) :: non_neg_integer | false
  def read_timer(timer_ref) do
    :erlang.read_timer(timer_ref)
  end

  @type spawn_opt  :: :link | :monitor | {:priority, :low | :normal | :high} |
                      {:fullsweep_after, non_neg_integer} |
                      {:min_heap_size, non_neg_integer} |
                      {:min_bin_vheap_size, non_neg_integer}
  @type spawn_opts :: [spawn_opt]

  @doc """
  Spawns the given module and function passing the given args
  according to the given options.

  The result depends on the given options. In particular,
  if `:monitor` is given as an option, it will return a tuple
  containing the pid and the monitoring reference, otherwise
  just the spawned process pid.

  It also accepts extra options, for the list of available options
  check [`:erlang.spawn_opt/4`](http://www.erlang.org/doc/man/erlang.html#spawn_opt-4).

  Inlined by the compiler.
  """
  @spec spawn((() -> any), spawn_opts) :: pid | {pid, reference}
  def spawn(fun, opts) do
    :erlang.spawn_opt(fun, opts)
  end

  @doc """
  Spawns the given module and function passing the given args
  according to the given options.

  The result depends on the given options. In particular,
  if `:monitor` is given as an option, it will return a tuple
  containing the pid and the monitoring reference, otherwise
  just the spawned process pid.

  It also accepts extra options, for the list of available options
  check [`:erlang.spawn_opt/4`](http://www.erlang.org/doc/man/erlang.html#spawn_opt-4).

  Inlined by the compiler.
  """
  @spec spawn(module, atom, list, spawn_opts) :: pid | {pid, reference}
  def spawn(mod, fun, args, opts) do
    :erlang.spawn_opt(mod, fun, args, opts)
  end

  @doc """
  The calling process starts monitoring the item given.
  It returns the monitor reference.

  See [the need for monitoring](http://elixir-lang.org/getting-started/mix-otp/genserver.html#the-need-for-monitoring)
  for an example.
  See [`:erlang.monitor/2`](http://www.erlang.org/doc/man/erlang.html#monitor-2) for more info.

  Inlined by the compiler.
  """
  @spec monitor(pid | {reg_name :: atom, node :: atom} | reg_name :: atom) :: reference
  def monitor(item) do
    :erlang.monitor(:process, item)
  end

  @doc """
  If `monitor_ref` is a reference which the calling process
  obtained by calling `monitor/1`, this monitoring is turned off.
  If the monitoring is already turned off, nothing happens.

  See [`:erlang.demonitor/2`](http://www.erlang.org/doc/man/erlang.html#demonitor-2) for more info.

  Inlined by the compiler.
  """
  @spec demonitor(reference) :: true
  @spec demonitor(reference, options :: [:flush | :info]) :: boolean
  def demonitor(monitor_ref, options \\ []) do
    :erlang.demonitor(monitor_ref, options)
  end

  @doc """
  Returns a list of process identifiers corresponding to all the
  processes currently existing on the local node.

  Note that a process that is exiting, exists but is not alive, i.e.,
  `alive?/1` will return `false` for a process that is exiting,
  but its process identifier will be part of the result returned.

  See [`:erlang.processes/0`](http://www.erlang.org/doc/man/erlang.html#processes-0) for more info.
  """
  @spec list :: [pid]
  def list do
    :erlang.processes()
  end

  @doc """
  Creates a link between the calling process and another process
  (or port) `pid`, if there is not such a link already.

  See [`:erlang.link/1`](http://www.erlang.org/doc/man/erlang.html#link-1) for more info.

  Inlined by the compiler.
  """
  @spec link(pid | port) :: true
  def link(pid) do
    :erlang.link(pid)
  end

  @doc """
  Removes the link, if there is one, between the calling process and
  the process or port referred to by `pid`. Returns `true` and does not
  fail, even if there is no link or `id` does not exist

  See [`:erlang.unlink/1`](http://www.erlang.org/doc/man/erlang.html#unlink-1) for more info.

  Inlined by the compiler.
  """
  @spec unlink(pid | port) :: true
  def unlink(pid) do
    :erlang.unlink(pid)
  end

  @doc """
  Associates the name with a pid or a port identifier. `name`, which must
  be an atom, can be used instead of the pid / port identifier with the
  `Kernel.send/2` function.

  `Process.register/2` will fail with `ArgumentError` if the pid supplied
  is no longer alive, (check with `alive?/1`) or if the name is
  already registered (check with `whereis/1`).
  """
  @spec register(pid | port, atom) :: true
  def register(pid, name) when not name in [nil, false, true] do
    :erlang.register(name, pid)
  end

  @doc """
  Removes the registered name, associated with a pid or a port identifier.

  See [`:erlang.unregister/1`](http://www.erlang.org/doc/man/erlang.html#unregister-1) for more info.
  """
  @spec unregister(atom) :: true
  def unregister(name) do
    :erlang.unregister(name)
  end

  @doc """
  Returns the pid or port identifier with the registered name.
  Returns `nil` if the name is not registered.

  See [`:erlang.whereis/1`](http://www.erlang.org/doc/man/erlang.html#whereis-1) for more info.
  """
  @spec whereis(atom) :: pid | port | nil
  def whereis(name) do
    nillify :erlang.whereis(name)
  end

  @doc """
  Returns the pid of the group leader for the process which evaluates the function.
  """
  @spec group_leader :: pid
  def group_leader do
    :erlang.group_leader
  end

  @doc """
  Sets the group leader of `pid` to `leader`. Typically, this is used when a processes
  started from a certain shell should have a group leader other than `:init`.
  """
  @spec group_leader(pid, leader :: pid) :: true
  def group_leader(pid, leader) do
    :erlang.group_leader(leader, pid)
  end

  @doc """
  Returns a list of names which have been registered using `register/2`.
  """
  @spec registered :: [atom]
  def registered do
    :erlang.registered()
  end

  @typep process_flag :: :trap_exit | :error_handler | :min_heap_size |
                         :min_bin_vheap_size | :priority | :save_calls |
                         :sensitive
  @doc """
  Sets certain flags for the process which calls this function.
  Returns the old value of the flag.

  See [`:erlang.process_flag/2`](http://www.erlang.org/doc/man/erlang.html#process_flag-2) for more info.
  """
  @spec flag(process_flag, term) :: term
  def flag(flag, value) do
    :erlang.process_flag(flag, value)
  end

  @doc """
  Sets certain flags for the process `pid`, in the same manner as `flag/2`.
  Returns the old value of the flag. The allowed values for `flag` are
  only a subset of those allowed in `flag/2`, namely: `save_calls`.

  See [`:erlang.process_flag/3`](http://www.erlang.org/doc/man/erlang.html#process_flag-3) for more info.
  """
  @spec flag(pid, :save_calls, non_neg_integer) :: non_neg_integer
  def flag(pid, flag, value) do
    :erlang.process_flag(pid, flag, value)
  end

  @doc """
  Returns information about the process identified by `pid` or `nil` if the process
  is not alive.
  Use this only for debugging information.

  See [`:erlang.process_info/1`](http://www.erlang.org/doc/man/erlang.html#process_info-1) for more info.
  """
  @spec info(pid) :: Keyword.t
  def info(pid) do
    nillify :erlang.process_info(pid)
  end

  @doc """
  Returns information about the process identified by `pid`
  or `nil` if the process is not alive.

  See [`:erlang.process_info/2`](http://www.erlang.org/doc/man/erlang.html#process_info-2) for more info.
  """
  @spec info(pid, atom | [atom]) :: {atom, term} | [{atom, term}]  | nil
  def info(pid, spec)

  def info(pid, :registered_name) do
    case :erlang.process_info(pid, :registered_name) do
      :undefined -> nil
      [] -> {:registered_name, []}
      other -> other
    end
  end

  def info(pid, spec) when is_atom(spec) or is_list(spec) do
    nillify :erlang.process_info(pid, spec)
  end

  @doc """
  Puts the calling process into a wait state
  where its memory allocation has been reduced as much as possible,
  which is useful if the process does not expect to receive any messages
  in the near future.

  See [`:erlang.hibernate/3`](http://www.erlang.org/doc/man/erlang.html#hibernate-3) for more info.

  Inlined by the compiler.
  """
  @spec hibernate(module, atom, list) :: no_return
  def hibernate(mod, fun, args) do
    :erlang.hibernate(mod, fun, args)
  end

  @compile {:inline, nillify: 1}
  defp nillify(:undefined), do: nil
  defp nillify(other),      do: other
end


Inline nillify in Process

Inline nillify in Process

defmodule Process do
  @moduledoc """
  This module provides convenience functions around processes and
  the process dictionary. In Erlang, most of these functions are
  auto-imported, but in Elixir they are grouped in a module for
  convenience. Notice that these functions, different from Erlang's,
  always return nil instead of undefined. You can use their Erlang
  version if you want the undefined value.
  """

  @doc """
  Returns true if the process exists and is alive, that is,
  is not exiting and has not exited. Otherwise, returns false.

  `pid` must refer to a process at the local node.
  """
  @spec alive?(pid) :: boolean
  def alive?(pid) do
    :erlang.is_process_alive(pid)
  end

  @doc """
  Returns all key-values in the dictionary.
  """
  @spec get :: [{term, term}]
  def get do
    :erlang.get()
  end

  @doc """
  Returns the value for the given key.
  """
  @spec get(term) :: term
  @spec get(term, default :: term) :: term
  def get(key, default // nil) do
    case :erlang.get(key) do
      :undefined ->
        default
      value ->
        value
    end
  end

  @doc """
  Returns all keys that have the given `value`.
  """
  @spec get_keys(term) :: [term]
  def get_keys(value) do
    :erlang.get_keys(value)
  end

  @doc """
  Stores the given key-value in the process dictionary.
  """
  @spec put(term, term) :: term | nil
  def put(key, value) do
    nillify :erlang.put(key, value)
  end

  @doc """
  Deletes all items in the dictionary.
  """
  @spec delete :: [{term, term}]
  def delete() do
    :erlang.erase()
  end

  @doc """
  Deletes the given key from the dictionary.
  """
  @spec delete(term) :: term | nil
  def delete(key) do
    nillify :erlang.erase(key)
  end

  @doc """
  Sends an exit signal with the given reason to the pid.

  The following behavior apply if reason is any term except `:normal` or `:kill`:

  1) If pid is not trapping exits, pid itself will exist with the given reason;

  2) If pid is trapping exits, the exit signal is transformed into a message
     {'EXIT', from, reason} and delivered to the message queue of pid;

  3) If reason is the atom `:normal`, pid will not exit. If it is trapping exits,
     the exit signal is transformed into a message {'EXIT', from, :normal} and
     delivered to its message queue;

  4) If reason is the atom `:kill`, that is if `exit(pid, :kill)` is called, an
     untrappable exit signal is sent to pid which will unconditionally exit with
     exit reason `:killed`.

  ## Examples

      Process.exit(pid, :kill)

  """
  @spec exit(pid, term) :: true
  def exit(pid, reason) do
    :erlang.exit(pid, reason)
  end

  @doc """
  Returns the pid of a new process started by the application of `fun`.
  It behaves exactly the same as `Kernel.spawn/1`.
  """
  @spec spawn((() -> any)) :: pid
  def spawn(fun) do
    :erlang.spawn(fun)
  end

  @type spawn_opt  :: :link | :monitor | {:priority, :low | :normal | :high} |
                      {:fullsweep_after, non_neg_integer} |
                      {:min_heap_size, non_neg_integer} |
                      {:min_bin_vheap_size, non_neg_integer}
  @type spawn_opts :: [spawn_opt]

  @doc """
  Returns the pid of a new process started by the application of `fun`.

  It also accepts extra options, for the list of available options
  check http://www.erlang.org/doc/man/erlang.html#spawn_opt-2
  """
  @spec spawn((() -> any), spawn_opts) :: pid | {pid, reference}
  def spawn(fun, opts) do
    :erlang.spawn_opt(fun, opts)
  end

  @doc """
  Returns the pid of a new process started by the application of
  `module.function(args)`. The new process created will be placed in the system
  scheduler queue and be run some time later.

  It behaves exactly the same as the `Kernel.spawn/3` function.
  """
  @spec spawn(module, atom, [any]) :: pid
  def spawn(mod, fun, args) do
    :erlang.spawn(mod, fun, args)
  end

  @doc """
  Returns the pid of a new process started by the application of
  `module.function(args)`. The new process created will be placed in the system
  scheduler queue and be run some time later.

  It also accepts extra options, for the list of available options
  check http://www.erlang.org/doc/man/erlang.html#spawn_opt-4

  """
  @spec spawn(module, atom, [any], spawn_opts) :: pid | {pid, reference}
  def spawn(mod, fun, args, opts) do
    :erlang.spawn_opt(mod, fun, args, opts)
  end

  @doc """
  Returns the pid of a new process started by the application of `fun`.
  A link is created between the calling process and the new
  process, atomically.
  """
  @spec spawn_link((() -> any)) :: pid
  def spawn_link(fun) do
    :erlang.spawn_link(fun)
  end

  @doc """
  Returns the pid of a new process started by the application of
  `module.function(args)`. A link is created between the calling process
  and the new process, atomically. Otherwise works like spawn/3.
  """
  @spec spawn_link(module, atom, [any]) :: pid
  def spawn_link(mod, fun, args) do
    :erlang.spawn_link(mod, fun, args)
  end

  @doc """
  Returns the pid of a new process started by the application of `fun`
  and reference for a monitor created to the new process.
  """
  @spec spawn_monitor((() -> any)) :: {pid, reference}
  def spawn_monitor(fun) do
    :erlang.spawn_monitor(fun)
  end

  @doc """
  A new process is started by the application of `module.function(args)`
  and the process is monitored at the same time. Returns the pid and a
  reference for the monitor. Otherwise works like spawn/3.
  """
  @spec spawn_monitor(module, atom, [any]) :: {pid, reference}
  def spawn_monitor(mod, fun, args) do
    :erlang.spawn_monitor(mod, fun, args)
  end

  @doc """
  The calling process starts monitoring the item given.
  It returns the monitor reference.

  See http://www.erlang.org/doc/man/erlang.html#monitor-2 for more info.
  """
  @spec monitor(pid | {reg_name :: atom, node :: atom} | reg_name :: atom) :: reference
  def monitor(item) do
    :erlang.monitor(:process, item)
  end

  @doc """
  If monitor_ref is a reference which the calling process
  obtained by calling monitor/1, this monitoring is turned off.
  If the monitoring is already turned off, nothing happens.

  See http://www.erlang.org/doc/man/erlang.html#demonitor-2 for more info.
  """
  @spec demonitor(reference) :: true
  @spec demonitor(reference, options :: [:flush | :info]) :: boolean
  def demonitor(monitor_ref, options // []) do
    :erlang.demonitor(monitor_ref, options)
  end

  @doc """
  Returns a list of process identifiers corresponding to all the
  processes currently existing on the local node.

  Note that a process that is exiting, exists but is not alive, i.e.,
  alive?/1 will return false for a process that is exiting,
  but its process identifier will be part of the result returned.

  See http://www.erlang.org/doc/man/erlang.html#processes-0 for more info.
  """
  @spec list :: [pid]
  def list do
    :erlang.processes()
  end

  @doc """
  Creates a link between the calling process and another process
  (or port) `pid`, if there is not such a link already.

  See http://www.erlang.org/doc/man/erlang.html#link-1 for more info.
  """
  @spec link(pid | port) :: true
  def link(pid) do
    :erlang.link(pid)
  end

  @doc """
  Removes the link, if there is one, between the calling process and
  the process or port referred to by `pid`. Returns true and does not
  fail, even if there is no link or `id` does not exist

  See http://www.erlang.org/doc/man/erlang.html#unlink-1 for more info.
  """
  @spec unlink(pid | port) :: true
  def unlink(pid) do
    :erlang.unlink(pid)
  end

  @doc """
  Associates the name with a pid or a port identifier. name, which must
  be an atom, can be used instead of the pid / port identifier in the
  send operator (name <- message).

  See http://www.erlang.org/doc/man/erlang.html#register-2 for more info.
  """
  @spec register(pid | port, atom) :: true
  def register(pid, name) do
    :erlang.register(name, pid)
  end

  @doc """
  Removes the registered name, associated with a pid or a port identifier.

  See http://www.erlang.org/doc/man/erlang.html#unregister-1 for more info.
  """
  @spec unregister(atom) :: true
  def unregister(name) do
    :erlang.unregister(name)
  end

  @doc """
  Returns the pid or port identifier with the registered name.
  Returns undefined if the name is not registered.

  See http://www.erlang.org/doc/man/erlang.html#whereis-1 for more info.
  """
  @spec whereis(atom) :: pid | port | nil
  def whereis(name) do
    nillify :erlang.whereis(name)
  end

  @doc """
  Returns the pid of the group leader for the process which evaluates the function.
  """
  @spec group_leader :: pid
  def group_leader do
    :erlang.group_leader
  end

  @doc """
  Sets the group leader of Pid to GroupLeader. Typically, this is used when a processes
  started from a certain shell should have another group leader than `:init`.
  """
  @spec group_leader(leader :: pid, pid) :: true
  def group_leader(leader, pid) do
    :erlang.group_leader(leader, pid)
  end

  @doc """
  Returns a list of names which have been registered using register/2.
  """
  @spec registered :: [atom]
  def registered do
    :erlang.registered()
  end

  @typep process_flag :: :trap_exit | :error_handler | :min_heap_size |
                         :min_bin_vheap_size | :priority | :save_calls |
                         :sensitive
  @doc """
  Sets certain flags for the process which calls this function.
  Returns the old value of the flag.

  See http://www.erlang.org/doc/man/erlang.html#process_flag-2 for more info.
  """
  @spec flag(process_flag, term) :: term
  def flag(flag, value) do
    :erlang.process_flag(flag, value)
  end

  @doc """
  Sets certain flags for the process Pid, in the same manner as flag/2.
  Returns the old value of the flag. The allowed values for Flag are
  only a subset of those allowed in flag/2, namely: save_calls.

  See http://www.erlang.org/doc/man/erlang.html#process_flag-3 for more info.
  """
  @spec flag(pid, process_flag, term) :: term
  def flag(pid, flag, value) do
    :erlang.process_flag(pid, flag, value)
  end

  @doc """
  Returns information about the process identified by pid.
  Use this only for debugging information.

  See http://www.erlang.org/doc/man/erlang.html#process_info-1 for more info.
  """
  @spec info(pid) :: Keyword.t
  def info(pid) do
    :erlang.process_info(pid)
  end

  @doc """
  Returns information about the process identified by pid
  or undefined if the process is not alive.

  See http://www.erlang.org/doc/man/erlang.html#process_info-2 for more info.
  """
  @spec info(pid, atom) :: {atom, term}
  def info(pid, spec) do
    :erlang.process_info(pid, spec)
  end

  @compile { :inline, nillify: 1 }
  defp nillify(:undefined), do: nil
  defp nillify(other),      do: other
end

defmodule Process do
  @moduledoc """
  This module provides convenience functions around processes and
  the process dictionary. In Erlang, most of these functions are
  auto-imported, but in Elixir they are grouped in a module for
  convenience. Notice that these functions, different from Erlang's,
  always return nil instead of undefined. You can use their Erlang
  version if you want the undefined value.
  """

  @doc """
  Returns true if the process exists and is alive, that is,
  is not exiting and has not exited. Otherwise, returns false.

  `pid` must refer to a process at the local node.
  """
  @spec alive?(pid) :: boolean
  def alive?(pid) do
    :erlang.is_process_alive(pid)
  end

  @doc """
  Returns all key-values in the dictionary.
  """
  @spec get :: [{term, term}]
  def get do
    :erlang.get()
  end

  @doc """
  Returns the value for the given key.
  """
  @spec get(term) :: term
  @spec get(term, default :: term) :: term
  def get(key, default // nil) do
    case :erlang.get(key) do
      :undefined ->
        default
      value ->
        value
    end
  end

  @doc """
  Returns all keys that have the given `value`.
  """
  @spec get_keys(term) :: [term]
  def get_keys(value) do
    :erlang.get_keys(value)
  end

  @doc """
  Stores the given key-value in the process dictionary.
  """
  @spec put(term, term) :: term | nil
  def put(key, value) do
    nillify :erlang.put(key, value)
  end

  @doc """
  Deletes all items in the dictionary.
  """
  @spec delete :: [{term, term}]
  def delete() do
    :erlang.erase()
  end

  @doc """
  Deletes the given key from the dictionary.
  """
  @spec delete(term) :: term | nil
  def delete(key) do
    nillify :erlang.erase(key)
  end

  @doc """
  Sends an exit signal with the given reason to the pid.

  The following behavior apply if reason is any term except `:normal` or `:kill`:

  1) If pid is not trapping exits, pid itself will exist with the given reason;

  2) If pid is trapping exits, the exit signal is transformed into a message
     {'EXIT', from, reason} and delivered to the message queue of pid;

  3) If reason is the atom `:normal`, pid will not exit. If it is trapping exits,
     the exit signal is transformed into a message {'EXIT', from, :normal} and
     delivered to its message queue;

  4) If reason is the atom `:kill`, that is if `exit(pid, :kill)` is called, an
     untrappable exit signal is sent to pid which will unconditionally exit with
     exit reason `:killed`.

  ## Examples

      Process.exit(pid, :kill)

  """
  @spec exit(pid, term) :: true
  def exit(pid, reason) do
    :erlang.exit(pid, reason)
  end

  @doc """
  Returns the pid of a new process started by the application of `fun`.
  It behaves exactly the same as `Kernel.spawn/1`.
  """
  @spec spawn((() -> any)) :: pid
  def spawn(fun) do
    :erlang.spawn(fun)
  end

  @type spawn_opt  :: :link | :monitor | {:priority, :low | :normal | :high} |
                      {:fullsweep_after, non_neg_integer} |
                      {:min_heap_size, non_neg_integer} |
                      {:min_bin_vheap_size, non_neg_integer}
  @type spawn_opts :: [spawn_opt]

  @doc """
  Returns the pid of a new process started by the application of `fun`.

  It also accepts extra options, for the list of available options
  check http://www.erlang.org/doc/man/erlang.html#spawn_opt-2
  """
  @spec spawn((() -> any), spawn_opts) :: pid | {pid, reference}
  def spawn(fun, opts) do
    :erlang.spawn_opt(fun, opts)
  end

  @doc """
  Returns the pid of a new process started by the application of
  `module.function(args)`. The new process created will be placed in the system
  scheduler queue and be run some time later.

  It behaves exactly the same as the `Kernel.spawn/3` function.
  """
  @spec spawn(module, atom, [any]) :: pid
  def spawn(mod, fun, args) do
    :erlang.spawn(mod, fun, args)
  end

  @doc """
  Returns the pid of a new process started by the application of
  `module.function(args)`. The new process created will be placed in the system
  scheduler queue and be run some time later.

  It also accepts extra options, for the list of available options
  check http://www.erlang.org/doc/man/erlang.html#spawn_opt-4

  """
  @spec spawn(module, atom, [any], spawn_opts) :: pid | {pid, reference}
  def spawn(mod, fun, args, opts) do
    :erlang.spawn_opt(mod, fun, args, opts)
  end

  @doc """
  Returns the pid of a new process started by the application of `fun`.
  A link is created between the calling process and the new
  process, atomically.
  """
  @spec spawn_link((() -> any)) :: pid
  def spawn_link(fun) do
    :erlang.spawn_link(fun)
  end

  @doc """
  Returns the pid of a new process started by the application of
  `module.function(args)`. A link is created between the calling process
  and the new process, atomically. Otherwise works like spawn/3.
  """
  @spec spawn_link(module, atom, [any]) :: pid
  def spawn_link(mod, fun, args) do
    :erlang.spawn_link(mod, fun, args)
  end

  @doc """
  Returns the pid of a new process started by the application of `fun`
  and reference for a monitor created to the new process.
  """
  @spec spawn_monitor((() -> any)) :: {pid, reference}
  def spawn_monitor(fun) do
    :erlang.spawn_monitor(fun)
  end

  @doc """
  A new process is started by the application of `module.function(args)`
  and the process is monitored at the same time. Returns the pid and a
  reference for the monitor. Otherwise works like spawn/3.
  """
  @spec spawn_monitor(module, atom, [any]) :: {pid, reference}
  def spawn_monitor(mod, fun, args) do
    :erlang.spawn_monitor(mod, fun, args)
  end

  @doc """
  The calling process starts monitoring the item given.
  It returns the monitor reference.

  See http://www.erlang.org/doc/man/erlang.html#monitor-2 for more info.
  """
  @spec monitor(pid | {reg_name :: atom, node :: atom} | reg_name :: atom) :: reference
  def monitor(item) do
    :erlang.monitor(:process, item)
  end

  @doc """
  If monitor_ref is a reference which the calling process
  obtained by calling monitor/1, this monitoring is turned off.
  If the monitoring is already turned off, nothing happens.

  See http://www.erlang.org/doc/man/erlang.html#demonitor-2 for more info.
  """
  @spec demonitor(reference) :: true
  @spec demonitor(reference, options :: [:flush | :info]) :: boolean
  def demonitor(monitor_ref, options // []) do
    :erlang.demonitor(monitor_ref, options)
  end

  @doc """
  Returns a list of process identifiers corresponding to all the
  processes currently existing on the local node.

  Note that a process that is exiting, exists but is not alive, i.e.,
  alive?/1 will return false for a process that is exiting,
  but its process identifier will be part of the result returned.

  See http://www.erlang.org/doc/man/erlang.html#processes-0 for more info.
  """
  @spec list :: [pid]
  def list do
    :erlang.processes()
  end

  @doc """
  Creates a link between the calling process and another process
  (or port) `pid`, if there is not such a link already.

  See http://www.erlang.org/doc/man/erlang.html#link-1 for more info.
  """
  @spec link(pid | port) :: true
  def link(pid) do
    :erlang.link(pid)
  end

  @doc """
  Removes the link, if there is one, between the calling process and
  the process or port referred to by `pid`. Returns true and does not
  fail, even if there is no link or `id` does not exist

  See http://www.erlang.org/doc/man/erlang.html#unlink-1 for more info.
  """
  @spec unlink(pid | port) :: true
  def unlink(pid) do
    :erlang.unlink(pid)
  end

  @doc """
  Associates the name with a pid or a port identifier. name, which must
  be an atom, can be used instead of the pid / port identifier in the
  send operator (name <- message).

  See http://www.erlang.org/doc/man/erlang.html#register-2 for more info.
  """
  @spec register(pid | port, atom) :: true
  def register(pid, name) do
    :erlang.register(name, pid)
  end

  @doc """
  Removes the registered name, associated with a pid or a port identifier.

  See http://www.erlang.org/doc/man/erlang.html#unregister-1 for more info.
  """
  @spec unregister(atom) :: true
  def unregister(name) do
    :erlang.unregister(name)
  end

  @doc """
  Returns the pid or port identifier with the registered name.
  Returns undefined if the name is not registered.

  See http://www.erlang.org/doc/man/erlang.html#whereis-1 for more info.
  """
  @spec whereis(atom) :: pid | port | nil
  def whereis(name) do
    nillify :erlang.whereis(name)
  end

  @doc """
  Returns the pid of the group leader for the process which evaluates the function.
  """
  @spec group_leader :: pid
  def group_leader do
    :erlang.group_leader
  end

  @doc """
  Sets the group leader of Pid to GroupLeader. Typically, this is used when a processes
  started from a certain shell should have another group leader than `:init`.
  """
  @spec group_leader(leader :: pid, pid) :: true
  def group_leader(leader, pid) do
    :erlang.group_leader(leader, pid)
  end

  @doc """
  Returns a list of names which have been registered using register/2.
  """
  @spec registered :: [atom]
  def registered do
    :erlang.registered()
  end

  @typep process_flag :: :trap_exit | :error_handler | :min_heap_size |
                         :min_bin_vheap_size | :priority | :save_calls |
                         :sensitive
  @doc """
  Sets certain flags for the process which calls this function.
  Returns the old value of the flag.

  See http://www.erlang.org/doc/man/erlang.html#process_flag-2 for more info.
  """
  @spec flag(process_flag, term) :: term
  def flag(flag, value) do
    :erlang.process_flag(flag, value)
  end

  @doc """
  Sets certain flags for the process Pid, in the same manner as flag/2.
  Returns the old value of the flag. The allowed values for Flag are
  only a subset of those allowed in flag/2, namely: save_calls.

  See http://www.erlang.org/doc/man/erlang.html#process_flag-3 for more info.
  """
  @spec flag(pid, process_flag, term) :: term
  def flag(pid, flag, value) do
    :erlang.process_flag(pid, flag, value)
  end

  @doc """
  Returns information about the process identified by pid.
  Use this only for debugging information.

  See http://www.erlang.org/doc/man/erlang.html#process_info-1 for more info.
  """
  @spec info(pid) :: Keyword.t
  def info(pid) do
    :erlang.process_info(pid)
  end

  @doc """
  Returns information about the process identified by pid
  or undefined if the process is not alive.

  See http://www.erlang.org/doc/man/erlang.html#process_info-2 for more info.
  """
  @spec info(pid, atom) :: {atom, term}
  def info(pid, spec) do
    :erlang.process_info(pid, spec)
  end

  defp nillify(:undefined), do: nil
  defp nillify(other),      do: other
end