There are only two mixed data types inside Erlang: List and Tuple, and neither of them supports naming access, so it is impossible to create an associative array like PHP, Ruby or Python (Hash in Ruby) without additional libraries.
In Ruby I can do this:
server_opts = {:port => 8080, :ip => '127.0.0.1', :max_connections => 10}
This expression is not supported at Erlang's grammar level
To avoid this limitation, the Erlang virtual machine provides a pseudo-data type called Record
Record supports named access, and later we will see why we call it "pseudo" data type
Define Record
Record is more similar to struct in C, rather than associative arrays, which must define the content from the beginning and only hold the data
Here is an example of the Record of a server's connection options:
-module(my_server).
-record(server_opts,
{port,
ip="127.0.0.1",
max_connections=10}).
% The rest of your code goes here.
Record uses the -record directive to declare. The first parameter is the name of the Record, and the second parameter is a Tuple. Tuple contains the field and default values in the Record.
Here we define the server_opts record, which has three fields: port, IP and maximum number of connections.
There is no default port, the default value of ip is "127.0.0.1", and the default value of max_connections is 10
Create Record
Record is created by using # symbols. Here is the legal way to create an instance of the Record server_opts:
Opts1 = #server_opts{port=80}.
This code creates a server_opts Record with port set to 80 and other fields use default values
Opts2 = #server_opts{port=80, ip="192.168.0.1"}.
This code creates a server_opts Record, but the ip is set to "192.168.0.1"
In short, when creating a record you can include any field, the omitted field will use the default value
Visit Record
The way of accessing Record is clumsy, if I want to access this field, I can do this:
Opts = #server_opts{port=80, ip="192.168.0.1"},
Opts#server_opts.port
Every time you want to access a record you must include the name of the record, why do you need this?
Because Record is not a real internal data type, it is just a little trick of the compiler.
Internally, Record is Tuple, as follows:
{server_opts, 80, "127.0.0.1", 10}
The compiler maps the Record name into Tuple
The Erlang virtual machine records the definition of Record, and the compiler translates all Record logic into Tuple logic
Therefore, there is no Record type at all, so every time you access a Record you have to tell Erlang which Record we are using (for the compiler is very unhappy, programmers are very upset)
Update Record
Updating Record is similar to creating Record:
Opts = #server_opts{port=80, ip="192.168.0.1"},
NewOpts = Opts#server_opts{port=7000}.
Here we first create a server_opts Record
NewOpts = Opts#{port=7000} Creates a copy of Opts and specifies port to 7000 and binds to NewOpts
Match Record and Guard statements
If you don't talk about pattern matching, it won't be considered Erlang
Let's take a look at an example:
handle(Opts=#server_opts{port=8000}) ->
% do special port 8080 stuff
handle(Opts=#server_opts{} ->
% default stuff
The Guard statement is similar to the above, for example, binding a port smaller than 1024 usually requires root permissions, so we can do this:
handle(Opts) when Opts#server_opts.port <= 1024 ->
% requires root access
handle(Opts=#server_opts{}) ->
% Doesn't require root access
Using Record
During my limited time using Erlang, I found that Record is mainly used in two scenarios
First, Record is used to save state, especially when using gen_server's behaviour
Since Erlang cannot maintain the state globally, the state must be passed before the method
Record can then be used to save configuration options, which can be considered a subset of the first point
Despite this, Record has some limitations, the most obvious thing is that it cannot add and delete fields at runtime. This is like C's struct, the structure of Record must be predefined.
If you want to add and remove fields at runtime, or you can determine which fields are available at runtime, you should usedictInstead of Record