emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Is there a function for auto currying in Elisp?


From: Stefan Monnier
Subject: Re: Is there a function for auto currying in Elisp?
Date: Fri, 22 Dec 2017 08:51:21 -0500
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/27.0.50 (gnu/linux)

>> Of course, we can do the easy cases:
>> 
>> (defun curry (f n)
>>   (if (< n 2)
>>       f
>>     (lambda (x)
>>       (curry (apply-partially f x) (- n 1)))))
> That's exactly the implementation I was playing with yesterday :-)
>> but ... I'm not sure we want to encourage this.
> Why not?

It's using a part of Elisp's implementation which is definitely not
very efficient.

If you're serious about using such a construct, you'd first want to make
it into a macro so it avoids the use of apply-partially which just adds
insult to injury.

>> What's your use case(s)?
> I don't have a specific use case right now, but I think that currying
> can be very expressive and elegant, and fits extremely well with
> functional programming, which Elisp is very capable of.

"Real" functional programming tends to use lots of small functions, so
it's important to optimize the implementation of function calls and
closure creations.  Elisp is not great at either of those:
- E.g. creating a closure with N free variables, in a straightforward
  implementation typically requires one allocation of an object of size
  N+1 words or so.  In Elisp, it requires allocation one 2 objects, one
  of size 6 (the `compiled-function`) and another of size N+M where M is
  the number of constants that appears within the function (and is
  typically larger than N).
- The above definition of `curry` eats a fair chunk of stack when you
  finally call the function: for a given N you end up with N nestings of
  `apply-partially`, each one eating some stack space.  If you use this
  heavily you're likely to want to bump the max stack depth.
- Of course, each nesting of `apply-partially` involves apply+&rest,
  hence conversion list<->vector which means allocation of `cons` cells
  (luckily those "vectors" are stack allocated so they cost a bit less).
- And of course, our implementation of function calls itself is not
  super efficient (several nested C function calls for each Elisp
  funcall, plus copying of arguments between different stacks), and all
  those lambda layerings exercise this weak spot of the language.

The Elisp style is evolving and those inefficiencies are more often
visible (e.g. cl-print's main bottleneck is the cost of apply+&rest in
my tests), so we can hope that someone will work on reducing them at
some point; and in general I do recommend to make your code clean and
correct first and foremost.  But keep in mind that Elisp's support for
functional programming is a bit lacking in efficiency.


        Stefan



reply via email to

[Prev in Thread] Current Thread [Next in Thread]