Currying for Clojure

203 views
Skip to first unread message

André Thieme

unread,
Oct 22, 2008, 5:24:47 PM10/22/08
to Clojure
I would like to suggest to improve currying.
Right now Clojure already comes with a nice way to do something that
is
nearly as good as currying, but still not fully.
In a very functional programming style this makes sense to have good
currying support.
Here is my proposal for extending the #(...) notation, and I would
like
to give some examples too:

1. Leave out all arguments after the last specific one was given.
This means we need to rewrite the curry notation into one that uses
apply. Examples:
#(< 5) instead of #(< 5 %) or #(< 5 %&)
#(< 5) ==> (fn [& args] (apply + 5 args))

If foo has arity 11:
#(foo 10 20) instead of
#(foo 10 20 %&) or, for those insisting on correct arity even
#(foo 10 20 %1 %2 %3 %4 %5 %6 %7 %8 %9)

#(foo 10 20) ==> (fn [& args] (apply foo 10 20 args))


2. If the value of an argument is not important use an underscore.
This will free us of uses of %1, %2, ...
Examples:
Create a function that will produce blue colors:
#(rgb _ _ 255) instead of
#(rgb %1 %2 255)

#(rgb _ _ 255) ==>
(fn [arg1 arg2 & args]
(apply rgb arg1 arg2 255 args))

Making use of point 1., now for creating green objects:
#(rgb _ 250) instead of
#(rgb %1 250 %2)

#(rgb _ 250) ==> (fn [arg1 & args] (apply rgb arg1 250 args))

3. Let's keep the possibility to list specific arguments as %1,
%2, ...
So we still want to have a function to square its argument:
#(* %1 %1) ==> (fn [arg1 & args] (apply * arg1 arg1 args))


4. Possibly allowing nested curries, where only the outer one
is allowed to list specific arguments %1, %2, %3, ...
All inner ones may only use the underscore syntax:

#(map #(+ 3) %1 %1) ==>
(fn [arg1 & args]
(apply map
(fn [& args] (apply + 3 args))
arg1
arg1))


As I see it the reader macro #(...) is right now implemented in
Assemb... in Java. So I can't give a patch for that, but instead
I hacked up this code fast, and it basically wraps the idea into
a macro:

(defn replace-placeholders [body]
((fn [args new-body [f & r :as b]]
(if (not b)
[(seq args) (seq new-body)]
(cond (= f '_) (let [gensym (gensym)]
(recur (conj args gensym) (conj new-body
gensym) r))
:default (recur args (conj new-body f) r))))
[] [] body))

(defn applyify [args body]
`(fn [~@args & args#]
(apply ~@body args#)))

(defmacro § [& body]
(let [[args body] (replace-placeholders body)]
(applyify args body)))


user> (filter (§ < _ 9) (range 20))
(0 1 2 3 4 5 6 7 8)

user> (map (§ * 3) (range 10))
(0 3 6 9 12 15 18 21 24 27)

I couldn't name the macro # as the reader would obviously try to
consume it first. So, if implemented as a reader macro we could
say §(< _ 9) or §(* 3) and such.
This version of replace-placeholders still misses the numbered
args %1, %2, .. as the reader would also steal those from me.
In the end the #(..) macro would do it, instead of §.

I would like to hear/read your opinions about support for currying
and extending the #() macro.
One thing I see as possibly critical is that my suggestion for
always using apply could potentially slow down code.
apply would have to do some extra work that is not needed when
a function is called directly.
How true is that, Rich?
I imagine situations where we
(map #(* 5) collection-of-10-million-numbers)
and get the apply runtime hit 10 mio times.

One way to improve this a little is to have all functions from the
standard
Clojure lib that take a fixed number of args and treat them
differently, and compile into code that makes no use of apply.
In such a case the 11-ary function foo would give us for
#(foo _ 10 20) ==>
(fn [a1 a2 a3 a4 a5 a6 a7 a8 a9]
(foo a1 10 20 a2 a3 a4 a5 a6 a7 a8 a9)) ; a direct call

This could be done only for the functions that come with Clojure
as those are guaranteed to always do the same and can't be over-
written by the user.
(defn + ...) is not possible ==>
java.lang.Exception: Name conflict, can't def + because namespace:
user refers to:#=(var clojure/+)

So, all functions written by the user must be called via apply if
we curry arguments away, even if the user provided function has a
fixed number of args, as the user could always exchange his old
function with a new one.

André Thieme

unread,
Oct 22, 2008, 5:40:01 PM10/22/08
to Clojure
On 22 Okt., 23:24, André Thieme <splendidl...@googlemail.com> wrote:

> This version of replace-placeholders still misses the numbered
> args %1, %2, .. as the reader would also steal those from me.
> In the end the #(..) macro would do it, instead of §.

So what I just did was extending it for this demostration by using
$1, $2, .. instead.

First we need two little helpers:
(defn digit-char? [char]
({\0 0 \1 1 \2 2 \3 3 \4 4 \5 5 \6 6 \7 7 \8 8 \9 9} char))

(defn numbered-arg? [arg]
(let [[$ & nums] (str arg)]
(and (= $ \$)
(every? digit-char? nums))))

Btw, something like digit-char? would be nice for the boot.clj

And now caring for the $n case:
(defn replace-placeholders2 [body]
((fn [args num-args new-body [f & r :as b]]
(if (not b)
[(seq args) (seq new-body)]
(cond (= f '_) (let [gensym (gensym)]
(recur (conj args gensym)
num-args
(conj new-body gensym)
r))
(numbered-arg? f) (if-let val (get num-args f)
(recur args num-args (conj new-body
val) r)
(let [gensym (gensym)]
(recur (conj args gensym)
(assoc num-args f gensym)
(conj new-body gensym)
r)))
:default (recur args num-args (conj new-body f) r))))
[] {} [] body))


As I see it this might even support nested currying if only the outer
one uses $1, $2, $3, ...

Example: #(map #(+ 3) %1 %1)

Translated §(map §(+ 3) $1 $1) and moved the § inside:

user> ((§ map (§ + 3) $1 $1) (range 1 10) (range 200 300) (range 5000
6000))
(5205 5209 5213 5217 5221 5225 5229 5233 5237)

wwmorgan

unread,
Oct 22, 2008, 6:28:10 PM10/22/08
to Clojure
You can get most of the functionality you're looking for with partial
(map (partial * 3) (range 10)) => (0 3 6 9 12 15 18 21 24 27)

(map (partial apply max 0) (partition 3 1 (range -5 5))) => (0 0 0 0 1
2 3 4)

André Thieme

unread,
Oct 22, 2008, 6:45:12 PM10/22/08
to Clojure
On 23 Okt., 00:28, wwmorgan <wmorga...@gmail.com> wrote:
> You can get most of the functionality you're looking for with partial

Yes sure. The thing is that currying is nothing but syntactical sugar.
It's not the functionality I am missing, but it’s brevity which makes
sense in functional programming style.

And partial doesn't allow me to have a simplified syntax for going
over specific arguments, which makes sense, so having this
throw-away underscore is nice IMO.
Also partial can't repeat arguments, what we now can do with %1, %1.

Rich Hickey

unread,
Oct 23, 2008, 9:12:13 AM10/23/08
to Clojure
I think, in general, this proposal, while interesting, has the
difficulty that it reduces the power of #() to not much more than
currying, and has some presumptions that need to be checked.

For instance, all examples were specific, without a generalization of
the functionality, which hides a problem:

#(...) currently expands to (fn [args*] (...))

I'm not sure there is a good general definition of what you have
described. It seems to presume that ... will be an ordinary function
call which can be converted into an apply. But it need not be so:

Special forms or macros are supported:

#(if %3 %2 %1)

as are methods:

#(.foo %)

and more complex expressions:

#(do (foo %2) (bar %1))

Rich

André Thieme

unread,
Oct 23, 2008, 3:49:17 PM10/23/08
to Clojure
On 23 Okt., 15:12, Rich Hickey <richhic...@gmail.com> wrote:
> On Oct 22, 6:45 pm, André Thieme <splendidl...@googlemail.com> wrote:

> I think, in general, this proposal, while interesting, has the
> difficulty that it reduces the power of #() to not much more than
> currying, and has some presumptions that need to be checked.

Well yes, I understand that point.
On the other hand the power of #() is not only reduced, but also
increased, by allowing things that it can't do right now.


> Special forms or macros are supported:
>
> #(if %3 %2 %1)

Okay, I see, and I didn’t know that.
What do you think about introducing another character that would
be treated special by the reader?
An example could be the paragraph symbol §
§(< 14)

That § could also be used later for an infix math package to allow
us to say §[4xy⁸ - 2.6√π]
(where the reader macro §[...] expects variable names to be one
char long.. this is just an example of a powerful infix macro).
The § is not often used in programs and would probably not be missed
very much.

> #(do (foo %2) (bar %1))

Yes, good example, so we should keep #(..) as it is now.
And I vote for adding currying support :-)
If the *readtable* were already accessible I would do it myself.
I understand that it currently has to be done in Java.

Matthew D. Swank

unread,
Oct 26, 2008, 1:23:52 AM10/26/08
to Clojure
On Oct 23, 8:12 am, Rich Hickey <richhic...@gmail.com> wrote:
> On Oct 22, 6:45 pm, André Thieme <splendidl...@googlemail.com> wrote:
>
> > On 23 Okt., 00:28, wwmorgan <wmorga...@gmail.com> wrote:
>
> > > You can get most of the functionality you're looking for with partial
>
> > Yes sure. The thing is that currying is nothing but syntactical sugar.
> > It's not the functionality I am missing, but it’s brevity which makes
> > sense in functional programming style.
>
> > And partial doesn't allow me to have a simplified syntax for going
> > over specific arguments, which makes sense, so having this
> > throw-away underscore is nice IMO.
> > Also partial can't repeat arguments, what we now can do with %1, %1.
>
> I think, in general, this proposal, while interesting, has the
> difficulty that it reduces the power of #() to not much more than
> currying, and has some presumptions that need to be checked.
>
> For instance, all examples were specific, without a generalization of
> the functionality, which hides a problem:
>
> #(...) currently expands to (fn [args*] (...))
>
> I'm not sure there is a good general definition of what you have
> described. It seems to presume that ... will be an ordinary function
> call which can be converted into an apply. But it need not be so:
>

I am dubious of the curry proposal, and there are already valid
questions about it raised in this thread. However, I am also reminded
of the "Design Rationale" section of http://srfi.schemers.org/srfi-26/srfi-26.html
which argues against an autocurrying mechanism for Scheme. While not
identical to what's being proposed here, the srfi and attendant
discussion is a good exploration of currying like mechanisms in a
language where primitive function application doesn't support it.

Matt

André Thieme

unread,
Oct 29, 2008, 3:35:47 PM10/29/08
to Clojure
On 26 Okt., 06:23, "Matthew D. Swank" <akopa.gmane.pos...@gmail.com>
wrote:

> I am dubious of the curry proposal,

Hi Matt!
Could you please be more specific about what makes you dubious?
I understand that the #(..) reader macro can’t be used, but this won’t
keep us
from having some other instead. It could be $(...) or ~(...) or @(...)
or *(...) or
!(...) or &(...) and so on.


> and there are already valid questions about it raised in this thread.

What were these questions?
wwmorgan mentioned that partial can be used to achieve part of what $
(..)
can do. I argue that (partial ...) makes not much sense.
Clojure has introduced a good bit of syntax in it’s reader, and it
makes totally
sense to have it. [1 2 3] instead of (vector 1 2 3).
^form instead of (meta form) and so on. Clojure is still Lisp, and in
my opinion
probably the best one we ever had. It’s an evolution and brings real
progress.
Rich mentioned that he finds the idea interesting but sees a technical
problem
of using the #(...) reader macro.
We will probably need something else, as #(..) also works with macros
and
special forms. So, let’s have $(..) then and make it working for
functions only.
We have no huge codebase of programs written in Clojure yet, so we
can’t do
a statistical analysis about uses of #(...). But I guess in most cases
it will not
be one of the special cases that Rich mentioned, but instead be used
as
syntactic sugar for (fn [..] ...). So, we could introduce a $(...) and
let it do
currying.


>  However, I am also reminded
> of the "Design Rationale" section ofhttp://srfi.schemers.org/srfi-26/srfi-26.html
> which argues against an autocurrying mechanism for Scheme.

Yes, I am also against autocurrying.
That’s why I suggest to make it a macro and introduce a reader macro
for it.
The code for the currying macro is in this thread.
One could rename it from § to curry or something like that.
I can’t implement a reader macro for it because the *readtable* is not
accessible.
So, we would probably need to put it into the Java code and make out
of
$(f %1 %1 _ 10) ==> (curry f %1 %1 _ 10) which would become ==>
(fn [arg1 arg2 & args] (apply f arg1 arg1 arg2 10 args)).

The code that I gave was a fast hack which doesn’t work with all
cases,
but it shows the concept.
Given that Clojure is the first modern and only real candidate (at
least for me) that
can outdate Common Lisp and already provides a rich syntax for the
most typical
stuff done in functional programming I honestly see no reason why
*not* also
offering support for explicit currying.
Explicit in that it will need a fancy symbol in front of the parens,
like $, to make
the reader catch and compile it. This also gives the eye something to
work with
and inform the reader/programmer that currying is happening here.


> While not
> identical to what's being proposed here, the srfi and attendant
> discussion is a good exploration of currying like mechanisms in a
> language where primitive function application doesn't support it.

It was a nice read, thanks for the link.
Reply all
Reply to author
Forward
0 new messages