auto-generated record access functions

Ulf Wiger (AL/EAB) ulf.wiger@REDACTED
Tue May 30 13:18:26 CEST 2006



Hi,

I modified the parse_transform somewhat so that it correctly handles
modules with no hand-written functions. My exhaustive testing was to
compile this module:

-module(test2).

-compile({parse_transform, exprecs}).


-record(r, {a1, a2}).


-export_records([r]).

and verify that the functions were generated appropriately.

Regarding the special requirements on exception semantics, my first
instinct is to stick to the default erlang semantics. This is only a
quick hack to solve a specific problem. You are of course free to extend
the parse_transform to suit your needs. To back up this claim, I've
inserted an EPL statement in the file.


BR,
Ulf W


> -----Original Message-----
> From: ke han [mailto:ke.han@REDACTED] 
> Sent: den 29 maj 2006 19:36
> To: Ulf Wiger (AL/EAB)
> Cc: erlang-questions@REDACTED
> Subject: Re: auto-generated record access functions
> 
> Ulf,
> thanks.  I'll look into this when I start again tomorrow.
> What I'm trying to do is use your record access in a 
> framework so the module person will use a record #person to 
> store its data.  In the simplest case, an "instance of 
> person" would be just the record which would be used in 
> conjunction with the person module to get and set values via 
> a controller which is a process.  More complex models could 
> be processes and would work in a similar fashion (the  
> controller would just have to know to send the message 
> differently).   
> I'm trying to figure out a way to make this somewhat 
> transparent to the framework user.
> 
> One thing I want to do with your record access is to start 
> off a model as an empty module definition (as in my example). 
>  As the programmer adds behavior (e.g. validations on the 
> various attributes of the record), they would add functions 
> to the module.
> So, I'm going to pretty quickly have to figure out how to 
> take your record access to the next level.
> 
> For example, start with the empty module person as:
> -module(person).
> -compile({parse_transform, exprecs}).
> -record(person, {firstName, lastName, birthDate}).
> -export_records([person]).
> 
> Then when the programmer is ready to add validation for the 
> birthDate, they add the function:
> 
> birthDate(Date) ->
> 	validateDate(Date).
> 
> This function would be not exported and called (when it 
> exists) by your record set function (set-person/2 in this 
> case).  This would also imply I want your record access 
> functions to handle exceptions in a certain way as well.  
> That is, if birthDate/1 throws an exception, the set does not happen.
> This provides the beginnings of an "attribute framework", 
> which is an essential part for easily creating models and controllers.
> 
> good night, ke han
> 
> 
> On May 30, 2006, at 12:28 AM, Ulf Wiger ((AL/EAB)) wrote:
> 
> >
> > I believe it's because your module has no hand-written functions.
> >
> > The parse-transform module I wrote looks for the first function in 
> > order to create the record access functions before it. If 
> there are no 
> > functions in the module, it will simply not generate the access 
> > functions, but it will go on and try to export them.
> >
> > That's not a feature. It didn't occur to me at the time that people 
> > would want to create modules with no functions in them. (:
> >
> > BR,
> > Ulf W
> >
> >> -----Original Message-----
> >> From: ke han [mailto:ke.han@REDACTED]
> >> Sent: den 29 maj 2006 16:09
> >> To: Ulf Wiger (AL/EAB)
> >> Cc: erlang-questions@REDACTED
> >> Subject: Re: auto-generated record access functions
> >>
> >> Ulf,
> >> I've just tested your record access generator.  I'm getting some 
> >> errors.
> >>
> >> I compile test.erl which is:
> >>
> >> -module(test).
> >> -compile({parse_transform, exprecs}).
> >> -record(person, {firstName, lastName, birthDate}).
> >> -export_records([person]).
> >>
> >> compile results:  (note, my erlc is a bash script which adds path, 
> >> includes and outputs in the correct places)  > erlc test.erl using 
> >> /Users/jhancock/bin/erlc
> >> ./test.erl:4: function '#new-person'/0 undefined
> >> ./test.erl:4: function '#set-person'/2 undefined
> >> ./test.erl:4: function '#get-person'/2 undefined
> >> ./test.erl:4: function '#info-person'/1 undefined
> >> ./test.erl:4: function '#new-person'/1 undefined
> >>
> >> I just tried your exact example from the April 19 posting.
> >> -module(test).
> >> -compile({parse_transform, exprecs}).
> >> -record(a, {a, b, c}).
> >> -export_records([a]).
> >>
> >> same type of errors.
> >>
> >> any ideas?
> >> thanks, ke han
> >>
> >>
> >>
> >>
> >> On Apr 19, 2006, at 12:35 AM, Ulf Wiger ((AL/EAB)) wrote:
> >>
> >>>
> >>> I've written a small(-ish) parse transform that generates 
> code for 
> >>> accessing exported records.
> >>>
> >>> I did read through ROK's abstract patterns proposal once 
> more, but 
> >>> decided that it would be too much to try to implement _that_. The 
> >>> current hack does use function names that are somewhat similar to 
> >>> ROK's abstract patterns, but I don't think that will be a problem 
> >>> (mainly because I don't envision that anyone would use this
> >> stuff if
> >>> the abstract patterns were
> >>> available.)
> >>>
> >>> The idea is to use records as before, but for module- local
> >> use. For
> >>> outside callers, records are 'exported'
> >>> by including a -export_records([RecName]) directive.
> >>> The following code extract illustrates the kind of code 
> laid out by 
> >>> the parse_transform:
> >>>
> >>> -module(test).
> >>> -compile({parse_transform, exprecs}).
> >>> -record(a, {a, b, c}).
> >>> -export_records([a]).
> >>>
> >>> %%% generated code
> >>> -export(['#new-a'/0, '#new-a'/1,
> >>>          '#get-a'/2, '#set-a'/2,
> >>>          '#info-a'/1]).
> >>>
> >>> '#new-a'() -> #a{}.
> >>> '#new-a'(Vals) -> '#set-a'(Vals, #a{}).
> >>>
> >>> '#get-a'(Attrs, R) when is_list(Attrs) ->
> >>>     ['#get-a'(A, R) || A <- Attrs];
> >>> '#get-a'(a, R) -> R#a.a;
> >>> '#get-a'(b, R) -> R#a.b;
> >>> '#get-a'(c, R) -> R#a.c.
> >>>
> >>> '#set-a'(Vals, Rec) ->
> >>>     F = fun ([], R, _F1) -> R;
> >>>             ([{a, V} | T], R, F1) -> F1(T, R#a{a = V}, F1);
> >>>             ([{b, V} | T], R, F1) -> F1(T, R#a{b = V}, F1);
> >>>             ([{c, V} | T], R, F1) -> F1(T, R#a{c = V}, F1)
> >>>         end,
> >>>     F(Vals, Rec, F).
> >>>
> >>> '#info-a'(fields) -> record_info(fields, a).
> >>>
> >>> %%% end generated code
> >>>
> >>>
> >>> A short shell dialogue to illustrate the use:
> >>>
> >>> Eshell V5.4.12  (abort with ^G)
> >>> 1> test:'#new-a'().
> >>> {a,undefined,undefined,undefined}
> >>> 2> test:'#set-a'([{a,1},{c,3}], v(1)).
> >>> {a,1,undefined,3}
> >>> 3> test:'#set-a'([{b,2}], v(2)).
> >>> {a,1,2,3}
> >>> 4> test:'#get-a'(b, v(3)).
> >>> 2
> >>> 5> test:'#get-a'([a,c], v(3)).
> >>> [1,3]
> >>> 6> test:'#info-a'(fields).
> >>> [a,b,c]
> >>>
> >>>
> >>> Parse transformery attached. It's based on the
> >> stools_id_trans module
> >>> that I posted a while back, but with improved error handling 
> >>> (controlled errors in the parse_transform module are reported as 
> >>> regular compilation
> >>> errors.)
> >>>
> >>> BR,
> >>> Ulf W
> >>> <exprecs.erl>
> >>
> >>
> 
> 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: exprecs.erl
Type: application/octet-stream
Size: 10731 bytes
Desc: exprecs.erl
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20060530/da513a7f/attachment.obj>


More information about the erlang-questions mailing list