Have pipeline in Erlang
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
examples
include
src
test
.travis.yml
LICENSE
README.md
rebar3
test.erl

README.md

pipeline travis test status Hex version

pipeline

By using this library you can pass result of an expression A as one parameter of another expression B and pass result of B as one parameter of C and so on. It's usefull in function call chaining. Isntead of writing:

foo(bar(baz(new_function(other_function())))).

Use erlang operator -- for pipelining and Just write:

other_function() -- new_function() -- baz() -- bar() -- foo().

By default result of every expression passes as last argument of next expression. Except first argument that can be anything, other arguments should be one of the following:

  • A function call (Mod:Func(Args) or Func(Args)).
    "Hello, world!\n" -- string:to_upper() -- io:format()
    
    %% If you want to pass result of one expression as different argument of next expression, use macro ?arg
    [1,2,3] -- length() -- element(?arg, {foo, bar, baz}) %% Gives baz
    
    %% Example of Replacing some items in a proplist
    [{name, foo}, {age, 23}, {location, earth}] --
    lists:keyreplace(name, 1, ?arg, {name, baz}) --       %% This function needs result of above expression as its third argument
    lists:keyreplace(age, 1, ?arg, {age, 18}) --          %% This function needs result of above expression as its third argument
    lists:keyreplace(location, 1, ?arg, {location, moon}) %% This function needs result of above expression as its third argument
  • A fun call.
    %% Example of Replacing some items in a proplist
    Opts = [{name, foo}, {age, 23}, {location, earth}],
    Replace = 
        fun(Key, Val, Opts2) ->
            lists:keyreplace(Key, 1, Opts2, {Key, Val})
        end,
    Opts -- Replace(name, baz) -- Replace(age, 18) -- Replace(location, moon)
    
    %% Terminate a child of supervisor if it was alive
    Terminate =
        fun
            ({_, Pid, _, _}) when is_pid(Pid) ->
                sys:terminate(Pid, normal);
            (_) ->
                ok
        end,
    SupRef -- supervisor:which_children() -- lists:keyfind(ChildId, 1) -- Terminate()
  • Parentheses containing an Operation with valid erlang operator and at least one macro ?arg in left or right of operator.
    %% Example of wrapping timestamp in milli-seconds
    {MegaSec, Sec, MicroSec} = os:timestamp(), 
    (MegaSec * 1000000) -- (?arg + Sec) -- (?arg * 1000000) -- (?arg + MicroSec) -- (?arg div 1000).

Example

Runnning above codes:

-module(test).

%% Don't forget to include pipeline header file for compiling correctly
-include_lib("pipeline/include/pipeline.hrl").


-export([print_hello_world/0
        ,replace/4
        ,terminate/2
        ,replace2/4
        ,timestamp/0]).


print_hello_world() ->
    "Hello, world!\n" -- string:to_upper() -- io:format().


replace(Name, Age, Location, Opts) ->
    Replace =
        fun(Key, Val, Opts2) ->
            lists:keyreplace(Key, 1, Opts2, {Key, Val})
        end,
    Opts -- Replace(name, Name) -- Replace(age, Age) -- Replace(location, Location).


terminate(SupRef, ChildId) ->
    Terminate =
        fun
            ({_, Pid, _, _}) when is_pid(Pid) ->
                sys:terminate(Pid, normal);
            (_) ->
                ok
        end,
    SupRef -- supervisor:which_children() -- lists:keyfind(ChildId, 1) -- Terminate().


replace2(Name, Age, Location, Opts) ->
    Opts --
    lists:keyreplace(name, 1, ?arg, {name, Name}) --
    lists:keyreplace(age, 1, ?arg, {age, Age}) --
    lists:keyreplace(location, 1, ?arg, {location, Location}).


timestamp() ->
    {MegaSec, Sec, MicroSec} = os:timestamp(),
    MegaSec -- (?arg * 1000000) -- (?arg + Sec) -- (?arg * 1000000) -- (?arg + MicroSec) -- (?arg div 1000).
Erlang/OTP 19 [erts-8.0] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V8.0  (abort with ^G)

1> c(test).
{ok,test}

2> test:print_hello_world().
HELLO, WORLD!
ok

3> test:replace(baz, 18, moon, [{name, foo}, {age, 23}, {location, earth}]).
[{name,baz},{age,18},{location,moon}]

4> supervisor:which_children(httpc_sup).    
[{httpc_handler_sup,<0.132.0>,supervisor,[httpc_handler_sup]},
 {httpc_profile_sup,<0.72.0>,supervisor,[httpc_profile_sup]}]

5> test:terminate(httpc_sup, httpc_handler_sup).
ok

6> supervisor:which_children(httpc_sup).        
[{httpc_handler_sup,<0.154.0>,supervisor,[httpc_handler_sup]}, %% Pid changed, then worked
 {httpc_profile_sup,<0.72.0>,supervisor,[httpc_profile_sup]}]

7> test:terminate(httpc_sup, foo). %% inexistent child
ok

8> test:replace2(baz, 18, moon, [{name, foo}, {age, 23}, {location, earth}]).
[{name,baz},{age,18},{location,moon}]

9> erlang:timestamp().
{1517,264503,800212}

10> test:timestamp().  
1517264504646

You can use this macro in blocks (case, if, begin, try and receive), argument of other function or fun call, body of fun. Don't use as element of tuple (also record), list or map. Then If you want to use operator -- with its default usage, just put that in one membered tuple or list. for example:

{"foo"} = {"foobar" -- "bar"},  

always test the code that includes pipeline's header file.

Debug

If you want to see processing and replaced expression, export ERL_PIPELINE_DEBUG and then compile the code:

~/projects/pipeline $ export ERL_PIPELINE_DEBUG=1
~/projects/pipeline $ rebar3 shell
===> Verifying dependencies...
===> Compiling pipeline
Erlang/OTP 19 [erts-8.3] [source] [64-bit] [smp:8:8] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V8.3  (abort with ^G)

1> c(test).
Processing expression "Hello, world!\n" -- string:to_upper() -- io:format() in line 15
New call io:format(string:to_upper("Hello, world!\n")) generated

Processing expression Opts
--
Replace(name, Name) -- Replace(age, Age) -- Replace(location, Location) in line 23
New call Replace(location,
        Location,
        Replace(age, Age, Replace(name, Name, Opts))) generated

Processing expression SupRef
--
supervisor:which_children() -- lists:keyfind(ChildId, 1) -- Terminate() in line 34
New call Terminate(lists:keyfind(ChildId, 1, supervisor:which_children(SupRef))) generated

Processing expression Opts
--
lists:keyreplace(name, 1, pipeline:argument(), {name,Name})
--
lists:keyreplace(age, 1, pipeline:argument(), {age,Age})
--
lists:keyreplace(location, 1, pipeline:argument(), {location,Location}) in line 38
New call lists:keyreplace(location,
                 1,
                 lists:keyreplace(age,
                                  1,
                                  lists:keyreplace(name,
                                                   1,
                                                   Opts,
                                                   {name,Name}),
                                  {age,Age}),
                 {location,Location}) generated

Processing expression MegaSec
--
pipeline:argument() * 1000000
--
pipeline:argument() + Sec
--
pipeline:argument() * 1000000
--
pipeline:argument() + MicroSec -- pipeline:argument() div 1000 in line 46
New call erlang:'div'(erlang:'+'(erlang:'*'(erlang:'+'(erlang:'*'(MegaSec,
                                                         1000000),
                                              Sec),
                                   1000000),
                        MicroSec),
             1000) generated
{ok,test}
2>

License

BSD 3-Clause

Author

pouriya.jahanbakhsh@gmail.com

Hex version

18.1.30