Skip to content

Instantly share code, notes, and snippets.

@garazdawi
Last active August 24, 2019 10:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save garazdawi/fed00fa0939624ae8109530afaeb50dd to your computer and use it in GitHub Desktop.
Save garazdawi/fed00fa0939624ae8109530afaeb50dd to your computer and use it in GitHub Desktop.
%% Introducing the new `apply` pattern match operand.
%% `apply` is used to delegate the handling of a match
%% to some other function including the action to be taken
%% when a successful match happens. The `apply` statement
%% replaces the "-> body" part of a function/case/receive.
%%
%% Simple example:
%% example() ->
%% Pat = fun(A) when is_atom(A) ->
%% {ok, A}
%% end,
%% example(Pat).
%%
%% example(Pat) ->
%% receive
%% Msg apply Pat(Msg)
%% end.
%%
%% This example creates a fun that is used at the pattern for
%% a receive in another function. Only messages that match
%% any clause in the fun will be received.
-module(funmatch).
-export([test/1, gen_server_test/0, match_many_dynamic/0]).
%% Small basic example
test(Parent) ->
%% Handle system messages and parent shutdown
Fun = fun({system, From, Msg}) ->
sys:handle_system_msg(Msg, From, Parent, ?MODULE,
[], [], false);
({'EXIT', P, Reason}) when P =:= Parent ->
exit(Reason)
end,
%% Use fun as match in a receive
receive
SysMsg apply Fun(SysMsg);
AnyMsg -> AnyMsg
end,
%% Use fun as match in a case
case hello of
Msg apply Fun(Msg);
hello -> ok
end.
%% An example of how it could be used in a gen_server like implementation
%% in order to do selective receives.
gen_server_test() ->
gen_server_test(0, fun handle_cast/2, fun handle_call/3).
gen_server_test(State, CastPat, CallPat) ->
%% Here we use a fun as part of the `when` in a match, this allows us
%% to transform some of the arguments and add some context to the calls.
%% Note that the messages are not matched unless both the receive
%% and handle_cast/call matched.
R = receive
{'$gen_cast', Msg} apply CastPat(Msg, State);
{'$gen_call', From, Msg} apply CallPat(Msg, From, State)
end,
case R of
{noreply, NewState} ->
gen_server_test(NewState, CastPat, CallPat);
{reply, Reply, NewState} ->
From ! Reply,
gen_server_test(NewState, CastPat, CallPat)
end.
%% if more then 10 incr calls are made, the increments are left in the
%% message queue of the process until a decr request comes along.
handle_cast(incr, State) when State < 10 ->
{noreply, State + 1};
handle_cast(decr, State) when State > 0 ->
{noreply, State - 1}.
handle_call(get, _From, State) ->
{reply, State, State}.
%% An example of how dynamic pattern matching in function heads might work
%% Match many_dynamic/1 could of course also be applied from a receive
%% clause.
match_many_dynamic() ->
Pats = [fun isatom/1, fun isnumber/1],
{atom, hello} = match_many_dynamic(Pats, hello),
{number, 123} = match_many_dynamic(Pats, 123),
{unexpected, []} = match_many_dynamic(Pats, []).
%% So this is kind of creepy and dangerous, recursive dynamic pattern matching.
%% If the pattern in the head of the list matches we run it,
%% otherwise if there is a tail we recursively match using the tail of the list.
%% If there is no match in the list of patterns we return {unexpected, Msg}.
%%
%% This of course means that we can have infinite recursive pattern matching
%% which may be a problem?
match_many_dynamic([HPat|_], Msg) apply HPat(Msg);
match_many_dynamic([_|TPat], Msg) apply match_many_dynamic(TPat, Msg);
match_many_dynamic([], Msg) ->
{unexpected, Msg}.
isatom(Atom) when is_atom(Atom) ->
{atom, Atom}.
isnumber(Number) when is_number(Number) ->
{number, Number}.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment