Thursday, August 17, 2006

Smerl Demo: Easy Function-Based Record Access in Erlang

A few people have told me that they thought Smerl was neat, but they didn't really know what it could be used for. So, I thought I'd share a short demo app that goes a little further than the code snippets in my last few postings about Smerl.

I've heard some people complain about Erlang' record access syntax. I agree with them that the notation Var#RecType.fieldname is not very pleasant to look at sometimes. With Smerl, we can make things better.

Here's a short module I wrote, called func_recs, which generates getters and setters for all records in a given source file:


generate(Filename) ->
{ok, Forms} = epp:parse_file(Filename, [], []),
fun({attribute, _Line, record, {RecName, RecFields}}) ->
case smerl:for_module(RecName) of
{ok, C1} ->
process_module(C1, RecFields);
_Err ->
process_module(smerl:new(RecName), RecFields)
(_Other) -> undefined
end, Forms).

process_module(MetaCtx, RecFields) ->
{_, C2} = lists:foldl(
fun({record_field, _Line,
{atom, _Line2, FieldName}},
{Idx, MetaCtx1}) ->
{Idx+1, process_field(MetaCtx1,
FieldName, Idx)};
({record_field, _Line,
{atom, _Line2, FieldName}, _Def},
{Idx, MetaCtx1}) ->
{Idx+1, process_field(MetaCtx1,
FieldName, Idx)}
end, {2, MetaCtx}, RecFields),

process_field(MetaCtx, FieldName, Idx) ->
L = 999,
Getter = {function,L,FieldName,1,

{ok, MetaCtx1} = smerl:add_func(MetaCtx, Getter),
Setter =
{ok, MetaCtx2} = smerl:add_func(MetaCtx1, Setter),

If the code path contains a module that has the same name as a record name in the source file, Smerl adds a getter and setter to the module for each field in the record. Otherwise, Smerl creates a new module purely in runtime.

Using func_recs is easy. Let's say we have the following source file called person.erl:


-record(person, {name, city}).

new() ->

We can now write the following code:

P = person:new(),
P1 = person:name(P, "Yannick"),
P2 = person:city(P1, "Barcelona"),
io:format("~w is from ~w", [person:name(P2), person:city(P2)])

This example doesn't exactly show you how to build a Google Killer in Erlang :) -- but I hope it illustrates some of the fun things you can do with Erlang metaprogramming using Smerl.

New tip (8/18/06): Are you looking an easy way of knowing what the abstract form for a function is? Fire up the erlang shell and type

io:parse_erl_form(". ").

This will give you a one-line mini shell, in which you can type your function and get back its abstract form representation.


bengt said...

i think that person.erl should export new/0, not new_person/0.

Yariv said...

Thanks! fixed.

Anders said...

While You are at it could You also add something like
new/1, that takes a list of {FieldName,Value} and initializes
the record with those values?

Yariv said...

Yes, of course! It wouldn't be too hard, either. To make it extra special, your meta-constructor would also implement the default field values for Erlang records if those values aren't set by the user. Maybe you can write that tutorial :)

Yariv said...

Read my latest entry on Smerl. I don't really like appending code snippets and I think I found a good way around either forms or code snippets.