Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

Experimental library to bring pipe and maybe operator equivalents in Erlang

License

NotificationsYou must be signed in to change notification settings

fenollp/fancyflow

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FancyFlow is an experimental Erlang library to bring convenience of things like the elixir pipe operator into Erlang, without needing to introduce new syntax (although we do play with existing stuff to avoid semantic confusion). It also allows some more flexibility by allowing the user to choose the placement of the state being weaved through.

It's a toy, but I'm open to feedback.

Usage

Add thefancyflow_trans to your modules or applications, and use any of the control flow functions:

[pipe](InitialState,Exp1,Exp2, ...,ExpN)[maybe](InitialState,Exp1,Exp2, ...,ExpN)[parallel](Exp1,Exp2, ...,ExpN)

Where each expression can be a valid Erlang expression, with the state substituted by the_ variable.

For example:

-module(fancyflow_demo).-export([sans_pipe/0,pipe/0,sans_maybe/0,maybe/0]).-compile({parse_transform,fancyflow_trans}).-specsans_pipe()->string().sans_pipe()->String="a b c d e f",string:join(lists:map(funstring:to_upper/1,string:tokens(String,"")),","    ).-specpipe()->string().pipe()->    [pipe]("a b c d e f",string:tokens(_,""),lists:map(funstring:to_upper/1,_),string:join(_,",")).-specsans_maybe()-> {ok,non_neg_integer()} | {error,term()}.sans_maybe()->casefile:get_cwd()of        {ok,Dir} ->casefile:read_file(filename:join([Dir,"demo","data.txt"]))of                {ok,Bin} ->                    {ok, {byte_size(Bin),Bin}};                {error,Reason} ->                    {error,Reason}end;        {error,Reason} ->            {error,Reason}end.-specmaybe()-> {ok,non_neg_integer()} | {error,term()}.maybe()->    [maybe](undefined,file:get_cwd(),file:read_file(filename:join([_,"demo","data.txt"])),            {ok, {byte_size(_),_}}).-specsans_parallel()-> [term() | {badrpc,term()}].sans_parallel()->R1=rpc:async_call(node(),lists,seq, [1,10]),R2=rpc:async_call(node(),filelib,wildcard, ["*"]),R3=rpc:async_call(node(),erlang,apply,                        [fun() ->timer:sleep(10),sleptend, []]),R4=rpc:async_call(node(),ets,all, []),    [rpc:yield(R1),rpc:yield(R2),rpc:yield(R3),rpc:yield(R4)].-specparallel()-> [{ok,term()} | {error,term()}].parallel()->    [parallel](lists:seq(1,10),filelib:wildcard("*"),begintimer:sleep(10),sleptend,ets:all()).

Thepipe() function reworks thesans_pipe() function to be equivalent. Themaybe() one gives the same result as thesans_maybe() one, although it ignores the initial state altogether by usingundefined. Thesans_parallel() one shows traditional rpc-based handling of parallel operations whereasparallel() shows a simpler syntax, without need of work-arounds for unexported calls.

The expressions must be literal, and may be nested although this isn't always super clear:

-specnested()-> [{ok,_} | {error,_}].nested()->    [parallel](%% first operation reads ./demo/data.txt        [maybe](undefined,file:get_cwd(),file:read_file(filename:join([_,"demo","data.txt"]))),%% second parallel op makes a filename and reads its size if any        [pipe]("a b c d e f",string:tokens(_,""),lists:map(funstring:to_upper/1,_),string:join(_,","),%% Maybe the file doesn't exist               [maybe](_,% the string from [pipe] is a filename herefile:read_file(_),                       {ok, {byte_size(_),_}})              )    ).

With a result set possibly looking like:

1> fancyflow_demo:nested().[{ok,{ok,<<"124567890\n">>}}, {ok,{error,enoent}}]

Showing what happens if the first file exists (contains1234567890\n) and the second one does not (filenameA,B,C,D,E,F), with both reads happening in parallel.

It might be a good idea not to nest the expressions too much.

How it works

Each form of

[pipe](InitialState,Exp1,Exp2, ...,ExpN)[maybe](InitialState,Exp1,Exp2, ...,ExpN)[parallel](Exp1,Exp2, ...,ExpN)

is translated to:

fancyflow:pipe(InitialState, [fun(Var) ->Exp1end,fun(Var) ->Exp2end,                              ...,fun(Var) ->ExpNend])fancyflow:maybe(InitialState, [fun(Var) ->Exp1end,fun(Var) ->Exp2end,                               ...,fun(Var) ->ExpNend])fancyflow:parallel([fun() ->Exp1end,fun() ->Exp2end,                    ...,fun() ->ExpNend])

Which internally, runs a fold over the functions based on the state. The variable used as a function head is generated dynamically (looks like_#Ref<0.0.3.1555>) such that they never conflict with the surrounding scope, and never complain if they are not used.

the[Function](...) syntax has been chosen to not look like a regular function call and so that nobody mistakes its unorthodox (and otherwise illegal) usage of free variables with normal Erlang code.

About

Experimental library to bring pipe and maybe operator equivalents in Erlang

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages


[8]ページ先頭

©2009-2025 Movatter.jp