[b] 2015 May 01
[m] 2015 May 03

Automatic Currying in Erlang

Yet another thing I often wish for in Erlang is staged application of any function without having to manually wrap it in stage funs (which you get in languages that feature curried-by-default functions, like OCaml and Haskell).

A few days ago it occurred to me that the ability to get fun arity info and to apply arguments as a list was all that was needed to get very, very close to the behavior I wanted. Voila:

-module(function).
-export([curry/1]).

curry(F) ->
    {arity, Arity} = erlang:fun_info(F, arity),
    curry(F, [], Arity).

curry(F, Args, 0) ->
    apply(F, lists:reverse(Args));
curry(F, Args, Arity) ->
    fun (X) -> curry(F, [X | Args], Arity - 1) end.

In action:

$ erl
1>
1> Nums = lists:seq(1, 10).
[1,2,3,4,5,6,7,8,9,10]
2>
2> Plus = function:curry(fun (X, Y) -> X + Y end).
#Fun
3>
3> lists:map(Plus(1), Nums).
[2,3,4,5,6,7,8,9,10,11]
4>
4> lists:map(Plus(3), Nums).
[4,5,6,7,8,9,10,11,12,13]
5>
5> lists:map(Plus(5), Nums).
[6,7,8,9,10,11,12,13,14,15]
6>
6> Fold = function:curry(fun lists:foldl/3).
#Fun
7>
7> AddTo = Fold(fun (X, Y) -> X + Y end).
#Fun
8>
8> AddTo0 = AddTo(0).
#Fun
9>
9> AddTo100 = AddTo(100).
#Fun
10>
10> AddTo0(Nums).
55
11> AddTo100(Nums).
155
12>
12> AddTo100(lists:seq(4, 78)).
3175
13>

My implementation of curry/1 now lives in the hope library's hope_fun module. See: https://github.com/xandkar/hope/commit/e033aadea66cc7f68e3a66cde23a2edfe4c4e9e6

While this probably isn't something you should run and convert all your Erlang code to, it is nice to have around when you know you want it.

Short discussion on the Erlang mailing list: https://groups.google.com/g/erlang-programming/c/sU7RtQm9qs0/m/8iQhPAA5l-cJ