[erlang-questions] Reusing patterns and guard sequences

Edward Blake <>
Sun Mar 8 05:40:05 CET 2015


Hello all,

Recently I've went and began implementing a concept preprocessor that
extends Erlang's pattern matching and guard syntax with constructs to make
bundles of patterns and guard sequences reusable, one of which resembles a
form of user-defined guard.

A few quick examples:

--------------------------

    %% example1.erl
    %%
    -module(example1).
    -export([ test/0 ]).
    -auto_include_udg([ \\non_empty_message/3, is_uep_displayable_uuid/1 ]).
    -record(message, { flags = 0, data = <<>>, uuid = <<>>}).

    test() ->
        receive
            Message \\ non_empty_message(UUID, MsgFlags, MsgLine)
              when MsgFlags /= 0, is_uep_displayable_uuid(UUID)
                                      orelse UUID =:= <<>> ->
                io:format("From UUID: ~s~n", [UUID]),
                io:format("Message: ~s", [MsgLine])
        after 1000 -> ok
        end.

--------------------------

    %% example2.erl
    %%
    -module(example2).
    -export([ test/1 ]).
    -auto_include_udg([ \\integer_in_second_place/1 ]).

    test(Compound \\ integer_in_second_place(Val)) -> Val.

--------------------------

For these examples the definitions for the pattern templates and guards are:

--------------------------

    %% non_empty_message.erg
    %%
    -user_defined_guard(non_empty_message, [
        \\non_empty_message/3 ]).

    (#message{ flags = MsgFlags, data = MsgLine, uuid = UUID })
        \\ non_empty_message(UUID, MsgFlags, MsgLine)
            when is_binary(MsgLine), MsgLine =/= <<>>.

--------------------------

    %% displayable_uuid.erg
    %%
    -user_defined_guard(displayable_uuid, [
        is_uep_displayable_uuid/6, is_uep_displayable_uuid/1,
        \\displayable_uuid/5, \\displayable_uuid/0 ]).

    (UUID \\ displayable_uuid(_,_,_,_,_)) \\ displayable_uuid().

    (<<UUID_Part_1:8/bytes,"-",
          UUID_Part_2:4/bytes,"-",
          UUID_Part_3:4/bytes,"-",
          UUID_Part_4:4/bytes,"-",
          UUID_Part_5:12/bytes>>) \\ displayable_uuid(
        UUID_Part_1, UUID_Part_2, UUID_Part_3, UUID_Part_4, UUID_Part_5).

    is_uep_displayable_uuid(UUID \\ displayable_uuid()).

    is_uep_displayable_uuid(
        UUID \\ displayable_uuid(UUID_Part_1, UUID_Part_2, UUID_Part_3,
UUID_Part_4, UUID_Part_5),
        UUID_Part_1,UUID_Part_2,UUID_Part_3,UUID_Part_4,UUID_Part_5).

--------------------------

    %% integer_in_second_place.erg
    %%
    -user_defined_guard(integer_in_second_place, [
        \\integer_in_second_place/1 ]).

    ([_,Value|_]) \\ integer_in_second_place(Value) := is_integer(Value);
    ({_,Value}) \\ integer_in_second_place(Value) := is_integer(Value);
    ({_,Value,_}) \\ integer_in_second_place(Value) when is_integer(Value).

--------------------------

A more thorough overview and description, as well as the preprocessor
source as it currently exists, can be found at the repository at:

    https://github.com/elblake/udg

The beginnings of a test suite is present which does some tests over a
series of source file scenarios, the loading of definition source files,
and modules requiring user input, one of which tests all files in a
directory recursively that preprocessed files are identical token-wise with
the original file, intended to see if the preprocessor splits and
recombines alternatives within function heads, cases, funs, receives, trys
and ifs correctly as well an indication if it could handle Erlang code in a
real world setting. I've passed that test over my erlang installation's lib
directory and they all pass. However the preprocessor does skip over
definitions that contain ? in some places of the function head since it
doesn't implement macro expansion.

And there is also another test early in design which randomly generates
arbitrary Erlang (albeit of a very small subset) source code with randomly
selected patterns and guards.

For the convenience of testing it out, an erlc_udg module takes as command
line input an erl file to preprocess it before compiling it with
compile:file.

I also apologize in advance for using the term "user-defined guard" for the
concept construct as that was what I was using as a working name during
implementation, I could have used another term but I couldn't come up with
one as of yet.

The preprocessor is a very early implementation and not complete in many
cases, some types of expressions are not parsed for the .erg definitions
for example, erroneous conditions in input source code simply causes
run-time errors without any presentation, and some scenarios with binaries
are a work in progress.

The (:=) operator was chosen to delimit the tail guard expression simply
because I wanted an erl_scan available operator that would go before the
disjunction operator to maintain a similarity to full head disjunctions of
functions, so having semicolons directly after 'when' wouldn't do, and
using (->) which normally hints to a sequence of general purpose
expressions for this purpose would be confusing.

Input is welcome.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20150307/9b5c2668/attachment.html>


More information about the erlang-questions mailing list