📜 ⬆️ ⬇️

Function call through a tuple

Not so long ago, I came across one interesting opportunity in Erlang. If instead of the name of the module when the function is called to pass a tuple, where the first element is the name of the module, the function will be called
The arity is one more than the called one, and the last argument is the same tuple.

Example:
-module(my_module). -export([test/2]). test(Arg1,{?MODULE,Arg2}) -> io:format("Arg1:~p~nArg2:~p~n",[Arg1,Arg2]). 

You can call as:
 my_module:test(1,{my_module,2}). 
or so
 {my_module,2}:test(1). 


How can this be applied?

For example, when creating abstract data types (data models) where in a tuple (or, more conveniently, in record -e) all data is stored.
 -module(user). -export([new/0,save/1]). -export([name/1,set_name/1]). -export([proplist/1]). % record name must be same as module name. -record(user,{id,name}). new() -> {ok,#user{}}. name(#user{name=Name}) -> Name. set_name(NewName) -> {ok,State#user{name=NewName}}. % … Some other code ... save(#user{id=undefined,name=Name} = State) -> % Create new object in db; % ... {ok,State}; save(#user{id=ID,name=Name} = State) -> % Update an object in database % ... {ok,State}; proplist(#user{id=ID,name=Name}) -> [{id,ID}, {name,Name}]. 

The result is the user type, which is convenient enough to use in the code:
 {ok,User} = user:new(), {ok,User2} = User:set_name("SomeName"), {ok,User3} = User2:save(), UserName = User3:name(). 

In principle, the idea is clear, but programming in this style is not the Erlang way, and in addition, static checking of the Dialyzer code is complicated . Plus, this code is different from this
 {ok,User} = user:new(), {ok,User2} = user:set_name("SomeName",User), {ok,User3} = user:save(User2), UserName = user:name(User3). 

only the number of characters. Therefore, I do not recommend using it everywhere.

I used this feature in my project only because of the possibility of creating a function over an abstract data type, knowing the access interface to it (for example, the proplist () function acts as an interface in the previous model).
')
When developing a RESTful service, it is very convenient to describe resources with models, and when released, convert them to the necessary types without knowing which module the model belongs to.
 to_json(Resource) -> Proplist = Resource:proplist(), Json = mochijson2_fork:encode({struct,[Proplist]}), {ok,Json}. to_xml(Resource) -> Proplist = Resource:proplist(), XML = SomeProplistToXmlGenerator(Proplist), {ok,XML}. 

Source: https://habr.com/ru/post/146471/


All Articles