Record

Module to work with, define and import records.

Records are simply tuples where the first element is an atom:

iex> Record.is_record {User, "john", 27}
true

This module provides conveniences for working with records at compilation time, where compile-time field names are used to manipulate the tuples, providing fast operations on top of the tuples’ compact structure.

In Elixir, records are used mostly in two situations:

  1. to work with short, internal data
  2. to interface with Erlang records

The macros defrecord/3 and defrecordp/3 can be used to create records while extract/2 can be used to extract records from Erlang files.

Types

Types can be defined for tuples with the record/2 macro (only available in typespecs). Like with the generated record macros it will expand to a tuple.

defmodule MyModule do
  require Record
  Record.defrecord :user, name: "john", age: 25

  @type user :: record(:user, name: String.t, age: integer)
  # expands to: "@type user :: {:user, String.t, integer}"
end

Summary

Functions

defrecord(name, tag \\ nil, kv)

Defines a set of macros to create and access a record

defrecordp(name, tag \\ nil, kv)

Same as defrecord/3 but generates private macros

extract(name, opts)

Extracts record information from an Erlang file

extract_all(opts)

Extracts all records information from an Erlang file

is_record(data)

Checks if the given data is a record

is_record(data, kind)

Checks if the given data is a record of kind

Functions

defrecord(name, tag \\ nil, kv) (macro)

Defines a set of macros to create and access a record.

The macros are going to have name, a tag (which defaults) to the name if none is given, and a set of fields given by kv.

Examples

defmodule User do
  require Record
  Record.defrecord :user, [name: "meg", age: "25"]
end

In the example above, a set of macros named user but with different arities will be defined to manipulate the underlying record:

# To create records
record = user()        #=> {:user, "meg", 25}
record = user(age: 26) #=> {:user, "meg", 26}

# To get a field from the record
user(record, :name) #=> "meg"

# To update the record
user(record, age: 26) #=> {:user, "meg", 26}

# Convert a record to a keyword list
user(record) #=> [name: "meg", age: 26]

The generated macros can also be used in order to pattern match on records and to bind variables during the match:

record = user() #=> {:user, "meg", 25}

user(name: name) = record
name #=> "meg"

By default, Elixir uses the record name as the first element of the tuple (the tag). But it can be changed to something else:

defmodule User do
  require Record
  Record.defrecord :user, User, name: nil
end

require User
User.user() #=> {User, nil}

Defining extracted records with anonymous functions

If a record defines an anonymous function, an ArgumentError will occur if you attempt to create a record with it. This can occur unintentionally when defining a record after extracting it from an Erlang library that uses anonymous functions for defaults.

Record.defrecord :my_rec, Record.extract(...)
#=> ** (ArgumentError) invalid value for record field fun_field,
cannot escape #Function<12.90072148/2 in :erl_eval.expr/5>.

To work around this error, redefine the field with your own &M.f/a function, like so:

defmodule MyRec do
  require Record
  Record.defrecord :my_rec, Record.extract(...) |> Keyword.merge(fun_field: &__MODULE__.foo/2)
  def foo(bar, baz), do: IO.inspect({bar, baz})
end

defrecordp(name, tag \\ nil, kv) (macro)

Same as defrecord/3 but generates private macros.

extract(name, opts)

Extracts record information from an Erlang file.

Returns a quoted expression containing the fields as a list of tuples. It expects the record name to be an atom and the library path to be a string at expansion time.

Examples

iex> Record.extract(:file_info, from_lib: "kernel/include/file.hrl")
[size: :undefined, type: :undefined, access: :undefined, atime: :undefined,
 mtime: :undefined, ctime: :undefined, mode: :undefined, links: :undefined,
 major_device: :undefined, minor_device: :undefined, inode: :undefined,
 uid: :undefined, gid: :undefined]

extract_all(opts)

Extracts all records information from an Erlang file.

Returns a keyword list containing extracted record names as keys, and lists of tuples describing the fields as values. It expects a named argument :from or :from_lib, which correspond to include or include_lib attribute from Erlang modules, respectively.

is_record(data) (macro)

Checks if the given data is a record.

This is implemented as a macro so it can be used in guard clauses.

Examples

iex> record = {User, "john", 27}
iex> Record.is_record(record)
true
iex> tuple = {}
iex> Record.is_record(tuple)
false

is_record(data, kind) (macro)

Checks if the given data is a record of kind.

This is implemented as a macro so it can be used in guard clauses.

Examples

iex> record = {User, "john", 27}
iex> Record.is_record(record, User)
true

© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.3.4/Record.html