[erlang-questions] Automatic currying

Siraaj Khandkar siraaj@REDACTED
Fri May 1 08:02:51 CEST 2015


On 4/30/15 8:46 PM, Richard A. O'Keefe wrote:
>
> On 1/05/2015, at 8:42 am, Siraaj Khandkar <siraaj@REDACTED> wrote:
>
>>
>>     curry(F) ->
>>         Info = erlang:fun_info(F),
>>         {value, {arity, Arity}} = lists:keysearch(arity, 1, Info),
>>         curry(F, [], Arity).
>
> You do know that you can write
> 	{arity, Arity} = erlang:fun_info(F, arity) ?

Did not. Nice!

>>     curry(F, Args, 0) ->
>>         apply(F, lists:reverse(Args));
>>     curry(F, Args, Arity) ->
>>         fun (X) -> curry(F, [X | Args], Arity - 1) end.
>
> This is where it gets hairy.  The thing is that in a language
> like Haskell or Clean, there is no difference between
>
>      f
>
> and
>
>      f {- applied to no arguments -}
>
> In Erlang, there is a big difference between
>
>      F
>
> and
>
>      F()

This comparison conflates explicit staged application with implicit 
delayed evaluation. Semantically, the unit is still there even in 
Haskell, as the last argument of the thunk, it's just that it is 
implicit and when it gets (ostensibly) applied is not up to you (yes, I 
know there're ways to force it, but that is besides this point).

Another strict language would be a better choice for comparison. Using 
SML, the equivalents would be:

Value:
   SML: f
   Erlang: F

Thunk activation:
   SML: f ()
   Erlang: F()

Very similar, with the unfortunate difference of  ()  not being a value 
in Erlang.

> What if I have
>
>      F = fun (X) -> ... end
>
> and I want
>
>      (curry(F))(X)
>
> to give me fun () -> ... X ... end?

If you actually do want the final output to be a thunk, then that thunk 
has to be returned by the original, non-curried, function. i.e. instead of

     fun (X) -> .. end

you can have

     fun (X) -> fun () -> .. end end

or, alternatively, just use a value of your choice as an ostensible unit:

     fun (X, unit) -> .. end
     fun (X, {}) -> .. end

So the last argument would have no meaning other than activation. Again, 
_if_ this is what you want - you ask for it explicitly.

> Having
>
>      curry(F, Args, 0) ->
>          Rev = lists:reverse(ARgs),
>          fun () -> apply(F, Rev).

This breaks the contract of currying - it does something I did not ask 
it to do.

> would allow people to invoke the result or not, as they chose.

The opposite - forcing a thunk on them would take the choice away from 
them (besides just breaking the contract).

> I suggest *not* doing any of this.
> Haskell tends to involve stuff like (`f`y) for
> providing the second argument, which looks distinctly
> odd when f has more than 2, and don't let's mention
> `flip' and its relatives.
>
> Why do I suggest not doing this?
> First, as already mentioned, the fact that Erlang
> functions are strict and effectful means that the
> distinction between F and F() is vital.

Addressed above. Currying has nothing to do with laziness.

> Second, this isn't really how Erlang is _meant_ to
> be used,

Erlang was not "meant" to be used in any of the ways it is being used 
right now. lambdas were added relatively late (which means it was not 
meant for any of the staple FP list processing functions like 
map/filter/fold).

> so unlike Haskell, the compiler is not
> going to put much effort into making this less slow.

Sure. We're no worse off here than with any other use of funs. If 
performance is an issue in some area of the code, than you optimize as 
needed. There's no conflict.

Remember, I'm not advocating for anything radical, like overlaying all 
existing functions with curry/1 and only using the curried form (that 
would be a horrible user experience, given the Erlang syntax). I'm 
simply sharing a technique which can be used when you do want it, 
explicitly and judiciously.

> Most important, other people aren't going to expect
> to read Erlang code written this way, so it's not
> going to be as maintainable as possibly longer code
> that's more direct.

As mentioned above - I'm not saying to curry all (or even most) 
functions this way - I'm saying that if/when one desires something like 
this - this is a convenient technique :)



More information about the erlang-questions mailing list