[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