Records, encapsulation and optional elements

Willem Broekema willem@REDACTED
Tue May 21 14:44:06 CEST 2002


Alex Peake wrote:
> I am new to Erlang. Could someone please advise if the following is a
> reasonable attempt at encapsulating the creation of records (with optional
> elements)? Is there a more elegant way? It seems a shame that I can define
> defaults (in the .hrl) and yet not use them (in an encapsulated way) in the
> .erl.

Until it's possible to use a variable in record_info/2 (so it's evaluated 
at run-time instead of compile-time), I use the following to avoid 
repeating code when assigning multiple attributes:

-record(r, {k1,k2,k3}).

test() ->
	F = record_info(fields, r),

	% Create an 'r' record:
	R = iter_fields(r, [{k1,v1},{k2,v2}], F),
	io:format("R: ~p~n", [R]),
	
	% Update the record, by supplying a {key,val} list:
	R2 = iter_fields(R, [{k2,newv2}, {k3,newv3}], F),
	io:format("R2: ~p~n", [R2]).


where 'iter_fields' is defined as follows:


% We make use of knowledge about implementation of records.
% It's bad style, but so very useful! ;-)
%
% Translate a {k,v} list 'L' to a record 'somerecord':
%   L = [ {k1,v1}, ... ]
%   Fields = record_info(fields, somerecord),
%   R = iter_fields(somerecord, L, Fields).

iter_fields(L, [] ) when list(L) ->
	[];
iter_fields( L, [F|Fields] ) when list(L) ->
	case lists:keysearch(F, 1, L) of
		{value, {F, Val}} ->
			[Val | iter_fields(L, Fields) ];
		false ->
			[undefined | iter_fields(L, Fields) ]
	end.


% Updating an existing record:
%  R = #somerecord{ k1=v1, k2=v2 },
%  Fields = record_info(fields, somerecord),
%  R2 = iter_fields(R, [{k2,v2new}], Fields)

iter_fields(RName, KVL, L) when atom(RName), list(KVL), list(L) ->
	% Assume 'RName' is record name, L its fieldlist
	list_to_tuple( [RName | iter_fields(KVL, L)] );
	
iter_fields(Old, KVL, L) when tuple(Old) ->
	% Assume 'Old' is a existing record, L its fieldlist.
	OldL = tuple_to_list(Old),
	list_to_tuple( [ hd(OldL) | iter_fields( tl(OldL), KVL, L) ] );

iter_fields(OldL, KVL, []) when list(OldL) ->
	[];
iter_fields( [O|OldL], KVL, [F|Fields] ) ->
	case lists:keysearch(F, 1, KVL) of
		{value, {F, Val}} ->
			% Field is overridden by KVL.
			[Val | iter_fields(OldL, KVL, Fields) ];
		false ->
			% Field value is not overridden, so use old val.
			[O | iter_fields(OldL, KVL, Fields) ]
	end.


- Willem




More information about the erlang-questions mailing list