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.