Record-like syntax for dictionaries

Anton Lavrik alavrik@REDACTED
Wed Jan 5 10:30:23 CET 2011


Hi all,

While playing with Erlang parse transformations, I came up with the
idea of using a record-like syntax for basic operations with
dictionaries from stdlib's "dict" module. Dictionary construction and
accessing elements by keys are conceptually similar to record
construction and accessing record fields, however standard dictionary
interface is rather cumbersome compared to Erlang record syntax.

I created a parse transformation to test this idea. This is a usage example:

-include_lib("erl_aliases.hrl").
-dict_alias('').
dict_alias_test() ->
    % creating an empty dictionary
    _ = #{},

    % associating foo with 1
    D = #{foo = 1},
    1 = D.foo,

    % setting foo to foo + 1 and baz to 100
    D1 = D#{foo = D.foo + 1}#{baz = 100},
    2 = D1.foo,
    100 = D1.baz,

    % accessing undefined field
    {'EXIT', {badarg, _}} = (catch D.bar),

    % several elements
    D2 = D#{bar = 1, fum = 10, obj = D},
    1 = D2.bar,
    10 = D2.fum,

    % accessing field of a dict included in another dict
    1 = D2.obj.foo,
    ok.

This is the result of the parse transformation:

dict_alias_test() ->
    _ = dict:new(),
    D = dict:store(foo, 1, dict:new()),
    1 = dict:fetch(foo, D),
    D1 =
        dict:store(baz, 100, dict:store(foo, dict:fetch(foo, D) + 1, D)),
    2 = dict:fetch(foo, D1),
    100 = dict:fetch(baz, D1),
    {'EXIT',{badarg,_}} = (catch dict:fetch(bar, D)),
    D2 = dict:store(obj, D, dict:store(fum, 10, dict:store(bar, 1, D))),
    1 = dict:fetch(bar, D2),
    10 = dict:fetch(fum, D2),
    1 = dict:fetch(foo, dict:fetch(obj, D2)),
    ok.

Note that in the beginning I defined a dict alias for an empty atom:
-dict_alias(''). If any other non-empty atom is used, then dictionary
construction and element access look exactly as correspondent record
operations. For example, with -dict_alias(dict), #dict{} will be
transformed to dict:new(), X#dict.a -- to dict:fetch(a, X) and so on.
This was the original idea, but then I discovered that it is possible
to get rid of record names, i.e. use #{} instead of #dict{} and X.foo
instead of X#dict.foo, etc. This, however, required definition of
three extra Yecc rules in erl_parse.yrl to handle expressions like
#{...}.

Obviously, such parse transformations work only with expressions --
they are not applied to guards and patterns.

The code is on GitHub (https://github.com/alavrik/erl_aliases), but
this functionality is not documented yet.

Overall, this looks pretty neat to me. Beyond simple use, I'm
thinking, for example, about converting arbitrary JSON objects to
dictionaries and then accessing their contents using this method.
Parsed JSON object keys can be converted to atoms using
binary_to_existing_atom.

What do you think? Is there anything I'm missing here that makes such
parse transformation potentially unreliable?

Anton


More information about the erlang-questions mailing list