This is not an answer but rather a complement to Alexey answer.
As he does, I was thinking that changing the parameters order should be enough to solve your issue. I had in mind an execution model where the variables are bound during the pattern matching of each function head, from left to right. So if the PlayerName
were bound in the first parameter match, it could be reused in the next variable as a map key to retrieve the PlayerStatus
.
It does not work like this. In fact, using a variable twice in the function head like in eq2(X,X) -> ...
is almost equivalent to add a guard condition like in eq1(Y,X) when Y =:= X -> ...
To illustrate this I wrote a minimal module with the version of code, compiled it and disassembled and commented the result. You will see that apart of an inversion of the parameter order for the comparison, the 2 codes are identical.
-module(bar).
-compile([export_all]).
eq1(Y, X) when Y =:= X -> Y;
eq1(_, _) -> false.
eq2(X, X) -> true;
eq2(_, _) -> false.
% result of "rp(beam_disasm:file(bar))." command in the shell
%
% {beam_file,bar,
% [{eq1,2,2},{eq2,2,5},{module_info,0,8},{module_info,1,10}],
% [{vsn,[196965018902586160349592469625435725005]}],
% [{options,[]},
% {version,"7.0.3"},
% {source,"c:/git/fourretout/src/bar.erl"}],
% [{function,eq1,2,2,
% [{label,1},
% {line,1},
% {func_info,{atom,bar},{atom,eq1},2},
% ^ ^ ^
% | | |_ arity
% | |________ fuction name
% |_________________ module name
% {label,2},
% ^
% |________________________________ first function head
% {test,is_eq_exact,{f,3},[{x,0},{x,1}]},
% ^ ^ ^ ^ ^
% | | | | |_ compare the second term of the function stack
% | | | |_______ with the first term of the function stack
% | | |______________ branch to label 3 if test fails
% | |_______________________ which test to perfom (here =:= exact equal)
% |________________________________ Instruction to perform: test
% return,
% ^
% |________________________________ Instruction to perform: return. the value returned is the first element of the
% function stack, in this case it is the first function parameter
% {label,3},
% ^
% |________________________________ second function head
% {move,{atom,false},{x,0}},
% ^ ^ ^
% | | |______________ where to move the value: the first element of the function stack
% | |_______________________ what to move: the atom false
% |________________________________ Instruction to perform: move a value to some place
% return]},
% ^ ^
% | |___________________________ last statement of the function
% |________________________________ Instruction to perform: return. the value returned is the first element of the
% function stack, in this case it is the atom false
% {function,eq2,2,5,
% [{line,2},
% {label,4},
% {func_info,{atom,bar},{atom,eq2},2},
% {label,5},
% {test,is_eq_exact,{f,6},[{x,1},{x,0}]},
% {move,{atom,true},{x,0}},
% return,
% {label,6},
% {move,{atom,false},{x,0}},
% return]},
% {function,module_info,0,8,
% [{line,0},
% {label,7},
% {func_info,{atom,bar},{atom,module_info},0},
% {label,8},
% {move,{atom,bar},{x,0}},
% {line,0},
% {call_ext_only,1,{extfunc,erlang,get_module_info,1}}]},
% {function,module_info,1,10,
% [{line,0},
% {label,9},
% {func_info,{atom,bar},{atom,module_info},1},
% {label,10},
% {move,{x,0},{x,1}},
% {move,{atom,bar},{x,0}},
% {line,0},
% {call_ext_only,2,{extfunc,erlang,get_module_info,2}}]}]}
Even with more complex function head, you can verify that the compiler works only by making test (comparison, type check, size...) on the function parameters or some term extract from the function parmeters.
Edit
Curiously, it could be done relatively easily, I have compiled the following code to assembler ( using the command c(bar1,['S'])
):
-module(bar1).
-compile([export_all]).
status(#{name := ChallengerName},{_Players, _Tables, ChallengerName}) ->
#{status => challenging};
status(#{name := PlayerName},{#{test := PlayerStatus}, _, _}) ->
{PlayerName,PlayerStatus};
status(_, _) ->
#{status => null}.
status1(Triple, #{name := Name}) ->
case Triple of
{_Players, _Tables, Name} -> #{status => challenging};
{#{Name := PlayerStatus}, _, _} -> PlayerStatus;
_ -> #{status => null}
end.
then modified the assembler file to:
{module, bar1}. %% version = 0
{exports, [{module_info,0},{module_info,1},{status,2},{status1,2}]}.
{attributes, []}.
{labels, 13}.
{function, status, 2, 2}.
{label,1}.
{line,[{location,"bar1.erl",5}]}.
{func_info,{atom,bar1},{atom,status},2}.
{label,2}.
{test,is_map,{f,4},[{x,0}]}.
{get_map_elements,{f,4},{x,0},{list,[{atom,name},{x,2}]}}.
{test,is_tuple,{f,4},[{x,1}]}.
{test,test_arity,{f,4},[{x,1},3]}.
{get_tuple_element,{x,1},0,{x,3}}.
{get_tuple_element,{x,1},2,{x,4}}.
{test,is_eq_exact,{f,3},[{x,4},{x,2}]}.
{move,{literal,#{status => challenging}},{x,0}}.
return.
{label,3}.
{test,is_map,{f,4},[{x,3}]}.
{get_map_elements,{f,4},{x,3},{list,[{x,2},{x,0}]}}.
// modif here: reuse the name that was stored in the third place {X,2}, assign the result to top of stack {x,0} and return if ok
return.
{label,4}.
{move,{literal,#{status => null}},{x,0}}.
return.
{function, status1, 2, 6}.
{label,5}.
{line,[{location,"bar1.erl",12}]}.
{func_info,{atom,bar1},{atom,status1},2}.
{label,6}.
{test,is_map,{f,5},[{x,1}]}.
{get_map_elements,{f,5},{x,1},{list,[{atom,name},{x,2}]}}.
{test,is_tuple,{f,8},[{x,0}]}.
{test,test_arity,{f,8},[{x,0},3]}.
{get_tuple_element,{x,0},0,{x,1}}.
{get_tuple_element,{x,0},2,{x,3}}.
{test,is_eq_exact,{f,7},[{x,3},{x,2}]}.
{move,{literal,#{status => challenging}},{x,0}}.
return.
{label,7}.
{test,is_map,{f,8},[{x,1}]}.
{get_map_elements,{f,8},{x,1},{list,[{x,2},{x,4}]}}.
{move,{x,4},{x,0}}.
return.
{label,8}.
{move,{literal,#{status => null}},{x,0}}.
return.
{function, module_info, 0, 10}.
{label,9}.
{line,[]}.
{func_info,{atom,bar1},{atom,module_info},0}.
{label,10}.
{move,{atom,bar1},{x,0}}.
{line,[]}.
{call_ext_only,1,{extfunc,erlang,get_module_info,1}}.
{function, module_info, 1, 12}.
{label,11}.
{line,[]}.
{func_info,{atom,bar1},{atom,module_info},1}.
{label,12}.
{move,{x,0},{x,1}}.
{move,{atom,bar1},{x,0}}.
{line,[]}.
{call_ext_only,2,{extfunc,erlang,get_module_info,2}}.
re-compile the module from the assembler ( c(bar1,[from_asm]).
) and then, in the shell it works as expected (in this code I have reverted the order of the parameters for the modified function status
compare to status1
from Alexey, but it was not necessary), I wonder if this kind of pattern matching will be available in a future release of the compiler?
1> M1 = #{name => "Name"}.
#{name => "Name"}
2> T_chal = {none,none,"Name"}.
{none,none,"Name"}
3> T_stat = {#{"Name" => "Status"},none,none}.
{#{"Name" => "Status"},none,none}
4> T_null = {#{"Other" => "no show"},none,"Unknown"}.
{#{"Other" => "no show"},none,"Unknown"}
5> c(bar1,[from_asm]).
{ok,bar1}
6> bar1:status1(T_chal,M1).
#{status => challenging}
7> bar1:status1(T_stat,M1).
"Status"
8> bar1:status1(T_null,M1).
#{status => null}
9> bar1:status(M1,T_chal).
#{status => challenging}
10> bar1:status(M1,T_stat).
"Status"
11> bar1:status(M1,T_null).
#{status => null}
12>