Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Workarounds for lack of varargs in Erlang

Completely new to Erlang. I'm trying to define some functions for function composition, such as compose, juxt and pipe but running into the fact that Erlang doesn't have (to my knowledge) varargs so it's difficult to write just one version of such functions that will work for all inputs.

So far my best idea is to hardcode functions of different arities up to a reasonable number, as well as providing a version that takes a list for anything bigger, like this:

pipe (X, Fs) when is_list(Fs) -> lists:foldl(fun (F, Acc) -> F(Acc) end, X, Fs);
pipe (X, F) -> F(X).
pipe (X, F, G) -> G(F(X)).
pipe (X, F, G, H) -> H(G(F(X))).
pipe (X, F, G, H, I) -> I(H(G(F(X)))).
pipe (X, F, G, H, I, J) -> J(I(H(G(F(X))))).
pipe (X, F, G, H, I, J, K) -> K(J(I(H(G(F(X)))))).
pipe (X, F, G, H, I, J, K, L) -> L(K(J(I(H(G(F(X))))))).

which works, but I'm curious if there's a better way?

like image 310
Russell Avatar asked Mar 19 '13 22:03

Russell


2 Answers

The problem is that Erlang functions are uniquely identified by Name/Arity; io:format/1 and io:format/2, for instance, are two different functions. Hence, variadic functions simply don't fit into Erlang.

The cleanest solution is probably what you suggested. Another option is to write a custom parse transform (a module that rewrites the AST after parsing but before compilation) to catch and transform calls to the specific functions you want, letting others pass through unmolested. The transform could intercept calls like pipe(A1, A2, A3, ..., An) and rewrite them to pipe([A1, A2, A3, ..., An]).

Be warned though: Parse transforms can be difficult to get right, and they must be explicitly referenced in every module that needs to use the features they provide. An example of parse transforms put to good use is Basho's Lager logging library.

like image 191
Martin Törnwall Avatar answered Nov 08 '22 11:11

Martin Törnwall


I wonder why the list notation is not good for you, because it gives a very short and easy implementation, similar to the io:format/2.

pipe (X, Fs) when is_list(Fs) -> lists:foldl(fun (F, Acc) -> F(Acc) end, X, Fs).
fibnext([A,B]) -> [A+B,A].


1> F = fun(X) -> my_module:fibnext(X) end.
#Fun<erl_eval.6.82930912>
2> my_module:pipe([1,1],lists:duplicate(12,F)).      
[377,233]
3>
like image 1
Pascal Avatar answered Nov 08 '22 11:11

Pascal