1%% Program showing the problems with record field accesses.
2
3-module(my_sofs).
4-export([ordset_of_sets/3, is_equal/2]).
5
6-define(TAG, 'Set').
7-define(ORDTAG, 'OrdSet').
8
9-record(?TAG, {data = [], type = type}).
10-record(?ORDTAG, {orddata = {}, ordtype = type}).
11
12-define(LIST(S), (S)#?TAG.data).
13-define(TYPE(S), (S)#?TAG.type).
14-define(SET(L, T), #?TAG{data = L, type = T}).
15-define(IS_SET(S), record(S, ?TAG)).
16
17%% Ordered sets and atoms:
18-define(ORDDATA(S), (S)#?ORDTAG.orddata).
19-define(ORDTYPE(S), (S)#?ORDTAG.ordtype).
20-define(ORDSET(L, T), #?ORDTAG{orddata = L, ordtype = T}).
21-define(IS_ORDSET(S), record(S, ?ORDTAG)).
22
23%% When IS_SET is true:
24-define(ANYTYPE, '_').
25-define(REL_TYPE(I, R), element(I, R)).
26-define(SET_OF(X), [X]).
27
28is_equal(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) ->
29    case match_types(?TYPE(S1), ?TYPE(S2)) of
30        true  -> ?LIST(S1) == ?LIST(S2);
31        false -> erlang:error(type_mismatch, [S1, S2])
32    end;
33is_equal(S1, S2) when ?IS_ORDSET(S1), ?IS_ORDSET(S2) ->
34    case match_types(?TYPE(S1), ?TYPE(S2)) of
35        true  -> ?ORDDATA(S1) == ?ORDDATA(S2);
36        false -> erlang:error(type_mismatch, [S1, S2])
37    end;
38is_equal(S1, S2) when ?IS_SET(S1), ?IS_ORDSET(S2) ->
39    erlang:error(type_mismatch, [S1, S2]);
40is_equal(S1, S2) when ?IS_ORDSET(S1), ?IS_SET(S2) ->
41    erlang:error(type_mismatch, [S1, S2]).
42
43%% Type = OrderedSetType
44%%      | SetType
45%%      | atom() except '_'
46%% OrderedSetType = {Type, ..., Type}
47%% SetType = [ElementType]           % list of exactly one element
48%% ElementType = '_'                 % any type (implies empty set)
49%%             | Type
50
51ordset_of_sets([S | Ss], L, T) when ?IS_SET(S) ->
52    ordset_of_sets(Ss, [?LIST(S) | L], [[?TYPE(S)] | T]);
53ordset_of_sets([S | Ss], L, T) when ?IS_ORDSET(S) ->
54    ordset_of_sets(Ss, [?LIST(S) | L], [?ORDTYPE(S) | T]);
55ordset_of_sets([], L, T) ->
56    ?ORDSET(list_to_tuple(lists:reverse(L)), list_to_tuple(lists:reverse(T)));
57ordset_of_sets(_, _L, _T) ->
58    error.
59
60%% inlined.
61match_types(T, T) -> true;
62match_types(Type1, Type2) -> match_types1(Type1, Type2).
63
64match_types1(Atom, Atom) when is_atom(Atom) ->
65    true;
66match_types1(?ANYTYPE, _) ->
67    true;
68match_types1(_, ?ANYTYPE) ->
69    true;
70match_types1(?SET_OF(Type1), ?SET_OF(Type2)) ->
71    match_types1(Type1, Type2);
72match_types1(T1, T2) when tuple(T1), tuple(T2), size(T1) =:= size(T2) ->
73    match_typesl(size(T1), T1, T2);
74match_types1(_T1, _T2) ->
75    false.
76
77match_typesl(0, _T1, _T2) ->
78    true;
79match_typesl(N, T1, T2) ->
80    case match_types1(?REL_TYPE(N, T1), ?REL_TYPE(N, T2)) of
81        true  -> match_typesl(N-1, T1, T2);
82        false -> false
83    end.
84