Skip to content

Instantly share code, notes, and snippets.

@toraritte
Last active February 20, 2020 21:12
Show Gist options
  • Save toraritte/366620e2559df1b68532f95b0097434a to your computer and use it in GitHub Desktop.
Save toraritte/366620e2559df1b68532f95b0097434a to your computer and use it in GitHub Desktop.
Currying in Erlang
-module(f).
-export([curry/1]).

curry(AnonymousFun) ->
    {arity, Arity} =
        erlang:fun_info(AnonymousFun, arity),

    do_curry(AnonymousFun, Arity, [[], [], []]).

do_curry(Fun, 0, [Fronts, Middle, Ends] = X) ->
    % Fronts ++ Middle ++ ")" ++ Ends;
    [F, M, E] =
        lists:map(fun(L) -> string:join(L, "") end, X),
    Fstring =
        F ++ "Run(" ++ string:trim(M, trailing, ",") ++ ")" ++ E,

    {ok, Tokens, _} =
        erl_scan:string(Fstring ++ "."),
    {ok, Parsed} =
        erl_parse:parse_exprs(Tokens),

    FunBinding =
        erl_eval:add_binding(
          'Run',
          Fun,
          erl_eval:new_bindings()
        ),
    {value ,CurriedFun, _} =
        erl_eval:exprs(Parsed, FunBinding),

    CurriedFun;

do_curry(Fun, Arity, [Fronts, Middle, Ends]) ->
    VarName = [64 + Arity],
    NewFronts = ["fun(" ++ VarName ++ ") -> " | Fronts] ,
    NewMiddle = [VarName ++ ","|Middle],
    NewEnds = [" end"|Ends],
    do_curry(Fun, Arity-1, [NewFronts, NewMiddle, NewEnds]).

Usage (noise culled from shell output):

72> c("./f") % If `f.erl` is in the current dir that is.

73> F = f:curry(fun(A,B,C) -> A + B + C end).

74> F(1).
75> G = F(1).
76> G(2).
77> H = G(2).
78> H(3).
6

79> I = (F(1))(2).
80> I(3).
6

82> F2 = mtest:curry(fun erlang:'++'/2).  

83> F2("lofa").

84> (F2("lofa"))("miez").
"lofamiez"

85> ((f:curry(fun lists:map/2))((f:curry(fun filename:join/2))("dir")))(["a_file", "b_file"]).
["dir/a_file","dir/b_file"]

Useful resources:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment