[erlang-questions] Record fields

zxq9 zxq9@REDACTED
Fri Dec 11 12:24:05 CET 2015


On 2015年12月11日 金曜日 13:53:46 Kirill Ratkin wrote:
> Hi!
> 
> I'm trying to get records fields at runtime.
> If I have record 'test' and I can do:
> record_info(fields, test).
> 
> But if I do:
> R = test,
> record_info(fields, R).
> 
> I get error :
> * 1: illegal record info.
> 
> Documentation says: To each module using records, a pseudo function is
> added during compilation to obtain information about records.
> 
> It seems it's really 'pseudo' function ....
> 
> Is there alternative way to get record fields in runtime?

Not exactly, no. Records are converted to tuples, not hashes, and the way field labels are expanded is a transformation performed at compile time, when the code is converted and record syntax is all magicked away. Consider this:

=====================================================================

ceverett@REDACTED:~/Code/erlang$ cat recfind.erl
-module(recfind).
-export([find_by_phone/2, find_by_mail/2, fields/0]).

-record(contact, {fname, lname, phone=[], mail=[], city=[], street=[]}).

find_by_phone(Phone, AddressBook) ->
    find(Phone, #contact.phone, AddressBook).

find_by_mail(Mail, AddressBook) ->
    find(Mail, #contact.mail, AddressBook).                                                                                                                                                                                             

find(Value, Field, AddressBook) ->
    case lists:keyfind(Value, Field, AddressBook) of
        #contact{fname = Fname, lname = Lname} ->
            {Fname, Lname};
        false ->
            {error, not_found}
    end.

fields() ->
    Fields = record_info(fields, contact),
    ok = io:format("Fields: ~tp~n", [Fields]).

ceverett@REDACTED:~/Code/erlang$ erlc -E recfind.erl
ceverett@REDACTED:~/Code/erlang$ cat recfind.E
-file("recfind.erl", 1).

find_by_phone(Phone, AddressBook) ->
    find(Phone, 4, AddressBook).

find_by_mail(Mail, AddressBook) ->
    find(Mail, 5, AddressBook).

find(Value, Field, AddressBook) ->
    case lists:keyfind(Value, Field, AddressBook) of
        {contact,Fname,Lname,_,_,_,_} ->
            {Fname,Lname};
        false ->
            {error,not_found}
    end.

fields() ->
    Fields = [fname,lname,phone,mail,city,street],
    ok = io:format("Fields: ~tp~n", [Fields]).

module_info() ->
    erlang:get_module_info(recfind).

module_info(X) ->
    erlang:get_module_info(recfind, X).

=====================================================================

You see what happened in the function fields/0?

    Fields = record_info(fields, contact),
became
    Fields = [fname,lname,phone,mail,city,street],

and

    find(Mail, #contact.mail, AddressBook)
became
    find(Phone, 4, AddressBook).

and

    #contact{fname = Fname, lname = Lname}
became
    {contact,Fname,Lname,_,_,_,_}

So, the basic answer to your question is "no". But the more interesting answer is to ask you another question: What is the effect you are trying to achieve by using record_info/2 at runtime? Maybe a different data structure is better, maybe the #record.accessor syntax is sufficient, and maybe what you are trying to do can be done better some other way?

-Craig



More information about the erlang-questions mailing list