Различные представления для одной и той же записи erlang

Предположим, у меня есть запись

-record(expense, {uuid, amount, tags}).

Я хотел бы иметь отображаемую версию записи. Поле тегов содержит уникальные идентификаторы тегов. Я хотел бы отображать имя тегов в форме вместо уникальных идентификаторов. Как бы вы сделали это в Erlang? Обычно в языке ООП вы делаете ViewModel, чтобы иметь другую отображаемую версию одного и того же объекта.

Варианты 1 Использовать ту же запись с другим форматом данных, но я думаю, что это нарушит контракт интерфейса; никто не сможет узнать, какая у него версия записи.

Вариант 2 Создать еще одну запись

-record(expense_view1, {uuid, amount, tags}).

Но это создаст много дублированных записей.

Вариант 3. Используйте кортеж или карту. Кортеж трудно поддерживать, если я добавляю в запись больше полей, а карты не гарантируют безопасность имен полей.


person user2187032    schedule 15.02.2021    source источник


Ответы (2)


Поле тегов содержит уникальные идентификаторы тегов. Я хотел бы отображать имя тегов в форме вместо уникальных идентификаторов.

Как насчет этого:

-module(a).
-compile(export_all).

-record(expense, {uuid, amount, tags}).

show_action(#expense{uuid=UUID, amount=Amount, tags={A, B, C} }) ->
    TagConversions= #{1 => "Joe", 2 => "Tammy", 3 => "Bob"},
    A_Conv = maps:get(A, TagConversions, "Nathan"),
    B_Conv = maps:get(B, TagConversions, "Nathan"),
    C_Conv = maps:get(C, TagConversions, "Nathan"),
    io:format("~w, ~w, {~s,~s,~s}~n", 
              [UUID, Amount, A_Conv, B_Conv, C_Conv]).


go() ->
    Expense1 = #expense{uuid=1, amount=10, tags={1,2,3} },
    show_action(Expense1).

В оболочке:

12> c(a).  
a.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,a}

13> a:go().
1, 10, {Joe,Tammy,Bob}
ok

Я хотел бы иметь отображаемую версию записи. Но это создаст много дублированных записей.

Да, но разве в ООП нет множества объектов View, содержащих все или часть данных в объектах Model?

Варианты 1 Использовать ту же запись с другим форматом данных, но я думаю, что это нарушит контракт интерфейса; никто не сможет узнать, какая у него версия записи.

Вы можете структурировать запись, чтобы оставить пустое место для имен тегов, соответствующих идентификаторам тегов, а затем заполнить имена тегов, когда сможете:

-module(a).
-compile(export_all).

-record(tag, {id, name=""}).
-record(expense, {uuid, amount, tags}).

show_action(Expense = #expense{uuid=UUID, amount=Amount, tags={A, B, C} }) ->
    TagConversions= #{1 => "Joe", 2 => "Tammy", 3 => "Bob"},
    A_Conv = maps:get(A#tag.id, TagConversions, "Nathan"),
    B_Conv = maps:get(B#tag.id, TagConversions, "Nathan"),
    C_Conv = maps:get(C#tag.id, TagConversions, "Nathan"),
    io:format("~w, ~w, {~s,~s,~s}~n", 
              [UUID, Amount, A_Conv, B_Conv, C_Conv]),

    Expense#expense{tags={
                      A#tag{name=A_Conv},
                      B#tag{name=B_Conv},
                      C#tag{name=C_Conv}
                     }}.
go() ->

    Expense1 = #expense{uuid=1, amount=10, 
                        tags={#tag{id=1},
                              #tag{id=2},
                              #tag{id=3} }
                       },

    show_action(Expense1).

В оболочке:

5> c(a).
a.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,a}

6> a:go().
1, 10, {Joe,Tammy,Bob}
{expense,1,10,{{tag,1,"Joe"},{tag,2,"Tammy"},{tag,3,"Bob"}}}
person 7stud    schedule 15.02.2021

Вы можете выбрать режим просмотра в функции доступа:

-module (tuple).

-export ([get/1,get/2,start/0,stop/0]).


-record(expense, {uuid, amount, tags}).

%%%%%%%%% Interfaces %%%%%%%%%

% start the server with a static map
start() ->
    Pid =spawn(fun() -> loop(#{1 => one, 2 => two, 3 => three}) end),
    register(server, Pid).

stop() ->
    server ! stop.

% By default for "external users" get the view with value
get(T) ->
    get(T,value).

% for "internal usage" it is possible to choose either the id view or the value view
get(T,value) ->
    Values = lists:map(fun get_value/1, T#expense.tags),
    T#expense{tags = Values};
get(T,id) ->
    T.

%%%%%%%%% server %%%%%%%%%%
% the server is in charge to store the id => value association
% it could be also stored in an ETS, a database ...
loop(Ids) ->
    receive
        stop ->
            done;
        {From, get_value, Id} ->
            % the choice is made to do not crash if the id does not exist
            From ! {ok,maps:get(Id, Ids, undefined)},
            loop(Ids)
    end.

%%%%%%%%% private %%%%%%%%%

get_value(Id) ->
    server ! {self(), get_value, Id},
    receive
        {ok,Value} ->
            Value
    end.

который дает в оболочке:

1> c(tuple).                                                  
{ok,tuple}
2> rr(tuple).                                                 
[expense]
3> T = #expense{uuid = 12345, amount = 20000, tags = [1,3,4]}.
#expense{uuid = 12345,amount = 20000,tags = [1,3,4]}
4> tuple:start().                                             
true
5> tuple:get(T).                                              
#expense{uuid = 12345,amount = 20000,
         tags = [one,three,undefined]}
6> tuple:get(T,value).
#expense{uuid = 12345,amount = 20000,
         tags = [one,three,undefined]}
7> tuple:get(T,id).   
#expense{uuid = 12345,amount = 20000,tags = [1,3,4]}
8> tuple:stop().
stop
person Pascal    schedule 16.02.2021