< Back

2015-05-01

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) ->
    Info = erlang:fun_info(F),
    {value, {arity, Arity}} = lists:keysearch(arity, 1, Info),
    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

Now I've already gotten shots fired at me by the venerable ROK for wanting to do such things in Erlang (which I believe I adequately defended), but I'll be happy to read any other critiques - shoot me an email! :)