Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use record name as a parameter?

Tags:

erlang

Lets say I have a record:

-record(foo, {bar}).

What I would like to do is to be able to pass the record name to a function as a parameter, and get back a new record. The function should be generic so that it should be able to accept any record, something like this.

make_record(foo, [bar], ["xyz"])

When implementing such a function I've tried this:

make_record(RecordName, Fields, Values) ->
    NewRecord = #RecordName{} %% this line gives me an error: syntax error before RecordName

Is it possible to use the record name as a parameter?

like image 251
nagaru Avatar asked Nov 05 '10 05:11

nagaru


2 Answers

You can't use the record syntax if you don't have access to the record during compile time.

But because records are simply transformed into tuples during compilation it is really easy to construct them manually:

-record(some_rec, {a, b}).

make_record(Rec, Values) ->
    list_to_tuple([Rec | Values]).

test() ->
    R = make_record(some_rec, ["Hej", 5]),  % Dynamically create record
    #some_rec{a = A, b = B} = R,            % Access it using record syntax
    io:format("a = ~p, b = ~p~n", [A, B]).  

Alternative solution

Or, if you at compile time make a list of all records that the function should be able to construct, you can use the field names also:

%% List of record info created with record_info macro during compile time
-define(recs, 
    [
     {some_rec, record_info(fields, some_rec)},
     {some_other_rec, record_info(fields, some_other_rec)}
    ]).

make_record_2(Rec, Fields, Values) ->
    ValueDict = lists:zip(Fields, Values),

    % Look up the record name and fields in record list
    Body = lists:map(
         fun(Field) -> proplists:get_value(Field, ValueDict, undefined) end,
         proplists:get_value(Rec, ?recs)),

    list_to_tuple([Rec | Body]).

test_2() ->
    R = make_record_2(some_rec, [b, a], ["B value", "A value"]),
    #some_rec{a = A, b = B} = R,
    io:format("a = ~p, b = ~p~n", [A, B]).

With the second version you can also do some verification to make sure you are using the right fields etc.

Other tips

Other useful constructs to keep in mind when working with records dynamically is the #some_rec.a expression which evaluates to the index of the a field in some_recs, and the element(N, Tuple) function which given a tuple and an index returns the element in that index.

like image 72
Lii Avatar answered Nov 11 '22 23:11

Lii


This is not possible, as records are compile-time only structures. At compilation they are converted into tuples. Thus the compiler needs to know the name of the record, so you cannot use a variable.

You could also use some parse-transform magic (see exprecs) to create record constructors and accessors, but this design seems to go in the wrong direction. If you need to dynamically create record-like things, you can use some structures instead, like key-value lists, or dicts.

like image 26
Zed Avatar answered Nov 11 '22 23:11

Zed