Simple Dynamic Record Access
There is one thing I hate about Erlang, and it is records. It is not so much that the syntax is annoying (yet I am getting used to it), but you cannot use that syntax for dynamic field access; records are a compile-time feature.
So, without using Yariv's Smerl for something like this, I opted for a simpler solution. Records are tuples after all, and you can get record information in a list. So, you can use these two facts to create a function to get the field you specify. Not anything stellar, but for dynamic record access, it works:
I couldn't find something like "lists:find/2", so I implemented my own. But the main function is "get_rec_value/3". So you do this:
1> rd(person, {id, name, email}).
2> get_rec_value(name, #person{name="Brian"}, record_info(fields, person)).
"Brian"
For fun, I also did setting record value.
3> set_rec_value(email, "c", #person{name="Brian", id="1", email="b"}, record_info(fields, person)).
I know this stuff looks really simple, but since I thought this was a bit of a nuisance, the fact that I cannot access records without hardcoding the values, something needed to be written.
Did I miss something though? I'd like to know. Please comment. :)
So, without using Yariv's Smerl for something like this, I opted for a simpler solution. Records are tuples after all, and you can get record information in a list. So, you can use these two facts to create a function to get the field you specify. Not anything stellar, but for dynamic record access, it works:
find(Item, List) ->
find_(Item, List, 1).
find_(Item, [], _) -> not_found;
find_(Item, [H|T], Count) ->
case H of
Item ->
Count;
_ ->
find_(Item, T, Count+1)
end.
get_rec_value(Key, Rec, RecordInfo) ->
case find(Key, RecordInfo) of
not_found ->
undefined;
Num ->
element(Num+1, Rec)
end.
I couldn't find something like "lists:find/2", so I implemented my own. But the main function is "get_rec_value/3". So you do this:
1> rd(person, {id, name, email}).
2> get_rec_value(name, #person{name="Brian"}, record_info(fields, person)).
"Brian"
For fun, I also did setting record value.
set_rec_value(Key, Value, Rec, RecordInfo) ->
RecList = tuple_to_list(Rec),
case find(Key, RecordInfo) of
not_found ->
Rec;
Num ->
List1 = lists:sublist(RecList, Num),
List2 = lists:sublist(RecList, Num+2, length(RecList)),
tuple_to_list(List1 ++ [Value] ++ List2)
end.
3> set_rec_value(email, "c", #person{name="Brian", id="1", email="b"}, record_info(fields, person)).
I know this stuff looks really simple, but since I thought this was a bit of a nuisance, the fact that I cannot access records without hardcoding the values, something needed to be written.
Did I miss something though? I'd like to know. Please comment. :)
3 Comments:
Hello,
you could avoid the list:sublist operations and list concatenation with erlang:setelement.
Nils
Hi Brian.
I have written up a different approach to solving the same issue over here, if you are interested: http://chlorophil.blogspot.com/2007/04/dynamic-record-access-functions-with.html
Cheers,
Philip
for the update function:
(i) if the key does not exist wouldn't it be more erlang-like to fail?
(ii) seems like the final method should be list_to_tuple rather than tuple_to_list (or i am completely lost).
Post a Comment
<< Home