Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Erlang. Records. Existing of field

I need function which returns true, when record has certain field and vice versa. Example:

-record(robot, {name,
            type=industrial,
            hobbies,
            details=[]
            }).

Crusher = #robot{name="Crusher", hobbies=["Crushing people","petting cats"]}.

SomeFunction(Crusher, hobbies). %% returns true
SomeFunction(Crusher, fantasy). %% returns false

Sorry for my English. It was exercise in Functional Programming in my University. It was written for Lisp, where is simple solution. But for me it was required by Erlang. thank you very much for your help. I wrote some ugly function. It is not solution for my first question. It is enough for my teacher.

searchArray(_, []) -> false;
searchArray(X, [X|_]) -> true;
searchArray(X, [_|T]) -> searchArray(X, T).

existField(Field) ->
searchArray(Field, record_info(fields, robot)).

And I think, too, maps is much more usefull here.

like image 711
Сергей Яхницкий Avatar asked Dec 02 '14 18:12

Сергей Яхницкий


People also ask

How do you define an Erlang record?

11.1 Defining RecordsA record definition consists of the name of the record, followed by the field names of the record. Record and field names must be atoms. Each field can be given an optional default value. If no default value is supplied, undefined is used.

What do you mean by tuple and record?

Records are similar to tuples, in that they group together various data elements. A record has fields, and these are named. While tuples are an ordered collection of data values, a tuple is an unordered collection of labeled data values.


2 Answers

Erlang records Demystified

You should understand that a record in erlang is just syntactic sugar that will ultimately become a regular tuple.

I'm sure you've done your homeworks and did have a look at the official reference here.

Let's elaborate on what you found there and take your robot record as an example. If you add the fellowing code somewhere in your module

io:format("My happy robot is ~p", #robot{name="foo", type=some_atom}).

You'll hopefully see something similar to this

My happy robot is {robot, "foo", some_atom, undefined, []}

Notice that the record is a tuple of size NumberOfField + 1. With the record name as the first element (the guard). You'll also notice that there is no reference to the fields that you've declared. This is because the record notation is simply a way to access tuple elements by name, rather than by position.

OK, you've opened an erlang shell, tried the command and it didn't work, that's normal. The shell just don't know about records (newer versions can, but with some magic). If you try the command in a module, it will work just fine. What you should understand, is that once your module is compiled, all the records defined in it are expanded. The resulting compiled module has no references to records anymore, neither attributes nor code.

Robot = #robot{name="foo", type=some_atom},
Robot#robot.type
%% = "foo"
%% Internally, sometime during the parsing, the function
%% erl_expand_records:module/2 will be called and transform the code to
%% something equivalent to this:
Robot = {robot, "foo", some_type, undefined, []},
element(3, Robot).

Of course, what happens in reality is a little bit more complex because erl_expand_records:module/2 works on AbstractForms, but you get the idea.

Now to answer you're question. You have a couple different options and you should pick the one that best suites your needs:

Option 1: Mastering the default value

When you define your record fields, you can give them an optional default value. If you don't specify that value, erlang will use undefined.

Example:

-record(record_1, {foo=bar,baz}).
io:format("~p", #record_1{}).
%% Will print
{record_1, bar, undefined}

Notice how the second field is set to undefined. From here, to solution to your question is quite simple

is_defined(undefined)->false;
is_defined(_)->true.

%% And call it like this
is_defined(Crusher#robot.hobbies).

OK, OK. It looks quite different than what you've asked for. In fact you can skip the function altogether and use the record notation directly in your function and case clause matching patterns.

Option 2: Hacking with macros

You can also define a magic macro that implements the idea defined in option 1 like this:

-define(IS_DEFINED(rec, inst, field), (undefined==inst#rec.field)).
%% and then use it like this
?IS_DEFINED(robot, Crusher, hobbies)

Not exactly a function either, but it looks like one.

like image 84
sitifensys Avatar answered Sep 22 '22 08:09

sitifensys


You can use record_info(fields, Record) to get list of record fields. You need to know that records and this function is a little bit tricky. Records are translated to fixed size tuple during compilation and pseudo function record_info is added.

#robot{name="Crusher", hobbies=["Crushing people","petting cats"]}

becomes:

{robot, "Crusher", industrial, ["Crushing people","petting cats"], []}

If you want to share record you should put it in .hrl file and include it in each module you want to be able to construct it using "#record_name{}", extract elements with dot operator by field or use record_info.

Since release 17 we have maps which should work for you as well and won't damage your brain.

like image 45
Łukasz Ptaszyński Avatar answered Sep 18 '22 08:09

Łukasz Ptaszyński