--> macro proposal

701 views
Skip to first unread message

Greg

unread,
Jul 6, 2010, 1:00:59 AM7/6/10
to clo...@googlegroups.com
I love the -> and ->> macros, but the problem with them is that you're limited to the functions you can use. Either all of the functions must be functions where the argument is passed as the first argument, or they must all be functions where it's passed in at the end.

I'm making my way through the Joy of Clojure right now, and it mentions that some people actually use commas to sort of "hint" as to where the argument will go (with the -> and ->> macros). Why not then just use a macro that actually listens to your hints?

So I whipped out this --> macro that does just that:

(defmacro -->
([x] x)
([x form]
(if (seq? form)
(with-meta (replace `{~'_ ~x} form) (meta form))
(list form x)
)
)
([x form & more]
`(--> (--> ~x ~form) ~@more)
)
)

You use it like so:

user=> (--> 3 (+ 1 _ 4) (prn "answer:" _))
"answer:" 8
nil

Perhaps this could be added to the dash-arrow family of macros?

- Greg

Meikel Brandmeyer

unread,
Jul 6, 2010, 8:23:36 AM7/6/10
to Clojure
Hi,

this comes up once in a while. See eg. here for an in-depth
discussion: http://groups.google.com/group/clojure/browse_thread/thread/66ff0b89229be894/c3d4a6dae45d4852

Note, that you can ease your pain a little with #(): (-> 3 (#(+ 1 %
4)) (#(prn "answer:" %))). This is rather ugly, though.

Sincerely
Meikel

Greg

unread,
Jul 6, 2010, 11:41:08 AM7/6/10
to clo...@googlegroups.com
On Jul 6, 2010, at 8:23 AM, Meikel Brandmeyer wrote:

> Hi,
>
> this comes up once in a while. See eg. here for an in-depth
> discussion: http://groups.google.com/group/clojure/browse_thread/thread/66ff0b89229be894/c3d4a6dae45d4852

So why hasn't it been incorporated yet into the standard library?

The --> macro (or the let-> mentioned there) is more powerful than -> and ->>, and it also makes code more readable too. It seems strange therefore not to have one.

- Greg

> Note, that you can ease your pain a little with #(): (-> 3 (#(+ 1 %
> 4)) (#(prn "answer:" %))). This is rather ugly, though.
>
> Sincerely
> Meikel
>

> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en

Stuart Halloway

unread,
Jul 6, 2010, 11:47:23 AM7/6/10
to clo...@googlegroups.com
There is not general agreement that something like --> is more readable. (I for one disagree, at least so far.)

Stu

Meikel Brandmeyer

unread,
Jul 6, 2010, 11:55:55 AM7/6/10
to Clojure
Hi,

On Jul 6, 5:47 pm, Stuart Halloway <stuart.hallo...@gmail.com> wrote:

> There is not general agreement that something like --> is more readable. (I for one disagree, at least so far.)

And it's implementation is not that trivial:

user=> (macroexpand '(--> (launch-rockets-if-called-twice)
(call :first _ :second _)))
(call :first (launch-rockets-if-called-twice) :second (launch-rockets-
if-called-twice))

Maybe there is just not much need for such a macro (at least for me,
don't for the generality of users). 99% of my use cases can be handled
with -> or ->> (or a combination there of).

Sincerely
Meikel

Greg

unread,
Jul 6, 2010, 11:57:32 AM7/6/10
to clo...@googlegroups.com
On Jul 6, 2010, at 11:47 AM, Stuart Halloway wrote:

> There is not general agreement that something like --> is more readable. (I for one disagree, at least so far.)

I'm very curious as to why as I find it hard to even fathom how you could think it's less readable to be explicit about the location of the parameter.

Still, that doesn't change two facts:

1) I, and many others, find -> and ->> *less* readable because they do not indicate at all where the parameter is.

2) --> is more versatile than either -> and ->>.


So why keep it out of the core library?

- Greg

Stuart Halloway

unread,
Jul 6, 2010, 2:01:59 PM7/6/10
to clo...@googlegroups.com
(1) Clojure APIs are very careful about parameter order.
(2) -> and ->> encourage chains of operations that build on that parameter order.
(3) I haven't seen a lot of examples where something like --> solves real problems in code.

In my experience, unneeded versatility == support headache.

Greg

unread,
Jul 6, 2010, 1:58:35 PM7/6/10
to clo...@googlegroups.com
> And it's implementation is not that trivial:

Excellent point! I've modified --> so that now it's even more versatile and only evaluates the functions once.

Now this is possible:

user=> (--> 1 (+ 1 _) (do (println "sideeffect!") _) (hash-map :a _ :b (+ 3 _)) :b)
sideeffect!
5


It now requires an additional replace-all function (a modified replace):

(defn replace-all
[smap coll]
(if (vector? coll)
(reduce
(fn [v i]
(if-let [e (find smap (nth v i))]
(assoc v i (val e))
v
))
coll
(range (count coll))
)
(map
#(if-let [e (find smap %)]
(val e)
(if (seq? %)
(replace-all smap %)
%
)
)
coll
)
)
)

(defmacro -->
([x] x)
([x form]
(if (seq? form)

`(let [~'_ ~x] ~form)


(list form x)
)
)
([x form & more]

; cache the result of the first form
(let [_ `(--> ~x ~form)]
`(--> ~_ ~@more)
)
)
)

- Greg

Wilson MacGyver

unread,
Jul 6, 2010, 2:26:39 PM7/6/10
to clo...@googlegroups.com
On Tue, Jul 6, 2010 at 2:01 PM, Stuart Halloway
<stuart....@gmail.com> wrote:
> In my experience, unneeded versatility == support headache.

I couldn't agree more. I'm happy to see selection of what goes into
core and contrib has
become more selective.


--
Omnem crede diem tibi diluxisse supremum.

Greg

unread,
Jul 6, 2010, 2:04:31 PM7/6/10
to clo...@googlegroups.com
> It now requires an additional replace-all function (a modified replace):

Oops! No it doesn't. That's from earlier experimentation that I did, and it's not necessary at all with the latest version (you'll see the --> macro doesn't even call it. :-p

- Greg

Greg

unread,
Jul 6, 2010, 2:09:18 PM7/6/10
to clo...@googlegroups.com
On Jul 6, 2010, at 2:01 PM, Stuart Halloway wrote:

> (1) Clojure APIs are very careful about parameter order.

And what if you want to use a function outside of the Clojure API? Or a function *in* the Clojure API that doesn't follow the parameter order you want?


> (2) -> and ->> encourage chains of operations that build on that parameter order.

Why is that important?


> (3) I haven't seen a lot of examples where something like --> solves real problems in code.

I haven't coded long enough in Clojure to provide you with any examples, but it seems like hoping that the functions you're going to use are going to have the correct parameter order is silly. Why hope when you can guarantee it won't matter?

Anyways, you haven't seen a lot of examples simply because people don't have a --> to use. Thus they're are forced to work around it, for example by replacing calls to -> or ->> with the corresponding standard calls (postfix/prefix? don't remember what that style is called).

If it existed, you would see it being used.

- Greg

Greg

unread,
Jul 6, 2010, 2:36:33 PM7/6/10
to clo...@googlegroups.com
On Jul 6, 2010, at 2:26 PM, Wilson MacGyver wrote:

> On Tue, Jul 6, 2010 at 2:01 PM, Stuart Halloway
> <stuart....@gmail.com> wrote:
>> In my experience, unneeded versatility == support headache.
>
> I couldn't agree more. I'm happy to see selection of what goes into
> core and contrib has become more selective.


Yes, let's handicap ourselves and then disparage a useful macro as "unneeded."

The -> and ->> macros aren't needed either, so why are they there?

While we're at it, we should make it so that the + function takes only two arguments because any more leads to "unneeded versatility" and therefore, apparently, to "support headache." :-p

- Greg

>
> --
> Omnem crede diem tibi diluxisse supremum.
>

David Nolen

unread,
Jul 6, 2010, 4:00:57 PM7/6/10
to clo...@googlegroups.com
On Tue, Jul 6, 2010 at 2:36 PM, Greg <gr...@kinostudios.com> wrote:
On Jul 6, 2010, at 2:26 PM, Wilson MacGyver wrote:

> On Tue, Jul 6, 2010 at 2:01 PM, Stuart Halloway
> <stuart....@gmail.com> wrote:
>> In my experience, unneeded versatility == support headache.
>
> I couldn't agree more. I'm happy to see selection of what goes into
> core and contrib has become more selective.

Yes, let's handicap ourselves and then disparage a useful macro as "unneeded."

What handicap? You wrote a macro that works for you that you can use in your own code. Aren't macros great? :)
 
The -> and ->> macros aren't needed either, so why are they there?

I've found a real need for -> ->>. I haven't personally felt a strong desire for --> ... yet.
 
While we're at it, we should make it so that the + function takes only two arguments because any more leads to "unneeded versatility" and therefore, apparently, to "support headache." :-p

- Greg
 
Now you're just getting contentious ;)

On a style note, you should follow the Lisp convention on the location of closing parens. 

David

Wilson MacGyver

unread,
Jul 6, 2010, 4:04:00 PM7/6/10
to clo...@googlegroups.com
On Tue, Jul 6, 2010 at 2:36 PM, Greg <gr...@kinostudios.com> wrote:
> On Jul 6, 2010, at 2:26 PM, Wilson MacGyver wrote:
>
>> On Tue, Jul 6, 2010 at 2:01 PM, Stuart Halloway
>> <stuart....@gmail.com> wrote:
>>> In my experience, unneeded versatility == support headache.
>>
>> I couldn't agree more. I'm happy to see selection of what goes into
>> core and contrib has become more selective.
>
>
> Yes, let's handicap ourselves and then disparage a useful macro as "unneeded."
>
> The -> and ->> macros aren't needed either, so why are they there?
>
> While we're at it, we should make it so that the + function takes only two arguments because any more leads to "unneeded versatility" and therefore, apparently, to "support headache." :-p

You know, just because you wrote a macro that you love and it works
for you, doesn't
mean it should get into clojure.core :)

Mike Meyer

unread,
Jul 6, 2010, 4:14:15 PM7/6/10
to clo...@googlegroups.com, gr...@kinostudios.com
On Tue, 6 Jul 2010 14:09:18 -0400
Greg <gr...@kinostudios.com> wrote:
> On Jul 6, 2010, at 2:01 PM, Stuart Halloway wrote:
> > (3) I haven't seen a lot of examples where something like --> solves real problems in code.
>
> I haven't coded long enough in Clojure to provide you with any examples, but it seems like hoping that the functions you're going to use are going to have the correct parameter order is silly. Why hope when you can guarantee it won't matter?
>
> Anyways, you haven't seen a lot of examples simply because people don't have a --> to use. Thus they're are forced to work around it, for example by replacing calls to -> or ->> with the corresponding standard calls (postfix/prefix? don't remember what that style is called).
>
> If it existed, you would see it being used.

If that's true, then it should be easy to find places in either
clojure.contrib or the parts of clojure.core that are written in
clojure that could make use of it.

Have you checked for those?

<mike
--
Mike Meyer <m...@mired.org> http://www.mired.org/consulting.html
Independent Network/Unix/Perforce consultant, email for more information.

O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

ataggart

unread,
Jul 6, 2010, 4:25:47 PM7/6/10
to Clojure
We try to keep a civil tone here; please do likewise.

Greg

unread,
Jul 6, 2010, 4:11:24 PM7/6/10
to clo...@googlegroups.com
> You know, just because you wrote a macro that you love and it works
> for you, doesn't mean it should get into clojure.core :)

I agree 100%, which is why I've explained the reasons for the suggestion.

I did not simply say "OMG I made this awesome macro now include it!"

I gave explicit reasons on *why* it should be included and defended them rationally. The responses I've received so far in opposition have been devoid of any real substance other than, "I don't want to."

The -> and ->> macros are like the 'first' and 'last' functions.

It would be silly to have a Clojure with only 'first' and 'last' functions, and without an 'nth' function.

The --> macro is the 'nth' equivalent.

It is a generalized version of the -> and ->> macros, able to handle situations they can't, and it happens to also have the added benefit of more readable code.

Therefore I think it should be in the core.

- Greg

Greg

unread,
Jul 6, 2010, 4:16:37 PM7/6/10
to clo...@googlegroups.com
> Have you checked for those?

No, sorry, I think that's a rather unreasonable request. I'm not going to spend hours sifting through the core and contrib just to jerk out examples for you.

I'd rather use logic and reason to make my case.

- Greg

Michael Gardner

unread,
Jul 6, 2010, 4:32:05 PM7/6/10
to clo...@googlegroups.com
On Jul 6, 2010, at 3:16 PM, Greg wrote:

>> Have you checked for those?
>
> No, sorry, I think that's a rather unreasonable request. I'm not going to spend hours sifting through the core and contrib just to jerk out examples for you.
>
> I'd rather use logic and reason to make my case.

It's not unreasonable when you are arguing (rather vehemently) for its inclusion in clojure.core, for which there is a high bar. And logic alone isn't likely to be able to answer the question of whether enough people would use this macro to make its inclusion worthwhile.

Aaron Bedra

unread,
Jul 6, 2010, 4:34:00 PM7/6/10
to clo...@googlegroups.com
On 07/06/2010 04:16 PM, Greg wrote:
>> Have you checked for those?
>>
> No, sorry, I think that's a rather unreasonable request. I'm not going to spend hours sifting through the core and contrib just to jerk out examples for you.
>
> I'd rather use logic and reason to make my case.
>
I don't think that this was intended to be unreasonable. I believe the
motivation here is to help everyone understand the basis for your idea
with a concrete example. No one would want you to spend hours sifting
through code to prove a point. I would just start with a few reasonable
places and see if you can code up a quick example. I believe that this
is a fair request. I would very much like to see some specific examples
as it would help solidify your ideas.

Cheers,

Aaron

David Nolen

unread,
Jul 6, 2010, 4:40:21 PM7/6/10
to clo...@googlegroups.com
On Tue, Jul 6, 2010 at 4:16 PM, Greg <gr...@kinostudios.com> wrote:
> Have you checked for those?

No, sorry, I think that's a rather unreasonable request. I'm not going to spend hours sifting through the core and contrib just to jerk out examples for you.

I'd rather use logic and reason to make my case.

- Greg

Greg you're enthusiasm is appreciated. But this ML is filled with talented and smart people who have an equal grasp of logic and reason who have been using Clojure for a couple years now and they aren't clamoring to your nice snippet of code. That's something to consider.

It is interesting that this is not the first time such a feature has been requested. But again, I haven't ever had a need for such a general threading macro.

One nice side effect of -> and ->> over --> is that it encourages everyone to consider the position of their arguments. Uniformity is nice.

David 

Mike Meyer

unread,
Jul 6, 2010, 4:43:03 PM7/6/10
to clo...@googlegroups.com, gr...@kinostudios.com
On Tue, 6 Jul 2010 16:16:37 -0400
Greg <gr...@kinostudios.com> wrote:

> > Have you checked for those?
>
> No, sorry, I think that's a rather unreasonable request. I'm not going to spend hours sifting through the core and contrib just to jerk out examples for you.
>
> I'd rather use logic and reason to make my case.

The examples aren't for me, they're for *you*. They provide evidence
to back up the assumptions your logic and reason start from. To wit:
you claim that there's a lot of need for this type of macro. Others
disagree with that claim. This isn't a claim that logic or reason can
prove. Without some kind of hard evidence, whether it's true or not is
basically a shouting match. The best place to get hard evidence is
clojure.contrib and clojure.core.

Greg

unread,
Jul 6, 2010, 5:02:40 PM7/6/10
to clo...@googlegroups.com
Greg you're enthusiasm is appreciated. But this ML is filled with talented and smart people who have an equal grasp of logic and reason who have been using Clojure for a couple years now and they aren't clamoring to your nice snippet of code. That's something to consider.

It is indeed. I would be surprised though if I was the only person who sees --> as useful, and that brings us to your next sentence...

It is interesting that this is not the first time such a feature has been requested. But again, I haven't ever had a need for such a general threading macro.

I'll make a list here of the reasons given for Yay/Nay so far:

Nay:

1) "I haven't had a need for a general threading macro."
2) The response so far is negative (and consists of repeating point #1 above).

Yay:

1) This has been requested multiple times. (Which pretty much cancels out Nay #1 & #2).
2) --> is a generalized version of -> and ->>, and therefore useful in situations where you can't use -> or ->>. It is the 'nth' to 'first' & 'last'.
3) It makes code more readable by explicitly indicating where the argument is passed.


At least if I've outlined the positions correctly, it's pretty sad that this is being fought against so hard.

I'll make the additional points that:

- There are plenty of macros and functions in the core that I'm sure you've never used once, yet they are there for others who find them useful. So simply responding to my proposal by saying, "I don't think I need it" isn't a valid reason against its inclusion.

- Most of you have used the other threading macros (-> and ->>), so you do in fact encounter situations and examples where the --> macro could be used. If it existed in the core already I think there's a pretty good chance you would have even used it yourselves, and your code would then gain the added benefit of being more readable.

As you pointed out earlier though, macros are great, and I'm free to use this macro in my own code, so I will.

- Greg

Meikel Brandmeyer

unread,
Jul 6, 2010, 5:20:46 PM7/6/10
to clo...@googlegroups.com
Hi,

Am 06.07.2010 um 20:09 schrieb Greg:

> On Jul 6, 2010, at 2:01 PM, Stuart Halloway wrote:
>
>> (1) Clojure APIs are very careful about parameter order.
>
> And what if you want to use a function outside of the Clojure API?

This would be most likely java interop, ie. ->.

> Or a function *in* the Clojure API that doesn't follow the parameter order you want?

There the main arguments are 99% of the times the first or the last ones. So -> or ->> will work.

>> (2) -> and ->> encourage chains of operations that build on that parameter order.
>
> Why is that important?

Because consistency matters.

>> (3) I haven't seen a lot of examples where something like --> solves real problems in code.
>
> I haven't coded long enough in Clojure to provide you with any examples, but it seems like hoping that the functions you're going to use are going to have the correct parameter order is silly. Why hope when you can guarantee it won't matter?
>
> Anyways, you haven't seen a lot of examples simply because people don't have a --> to use. Thus they're are forced to work around it, for example by replacing calls to -> or ->> with the corresponding standard calls (postfix/prefix? don't remember what that style is called).
>
> If it existed, you would see it being used.

I don't think so. For example sequence or not-empty exist. But I never needed one of them in two and half years of Clojure coding. And I can't remember to have seen a single usage in other peoples code. (of course an absolutely representative sample... ;))

> Yes, let's handicap ourselves and then disparage a useful macro as "unneeded." The -> and ->> macros aren't needed either, so why are they there? While we're at it, we should make it so that the + function takes only two arguments because any more leads to "unneeded versatility" and therefore, apparently, to "support headache." :-p

Can we tune down the rethoric a little bit? These issues were discussed in depth several times now. And the fact that such a macro was not included in core should give a hint, that the pain can't be that big.

Sincerely
Meikel

David Nolen

unread,
Jul 6, 2010, 5:22:11 PM7/6/10
to clo...@googlegroups.com
On Tue, Jul 6, 2010 at 5:02 PM, Greg <gr...@kinostudios.com> wrote:

I'll make a list here of the reasons given for Yay/Nay so far:

Nay:

1) "I haven't had a need for a general threading macro."
2) The response so far is negative (and consists of repeating point #1 above).

3) It would encourage people to not follow Clojure's conventions around argument positions for fns that deal with sequences/collections.

That is a pretty important Nay and illustrates that --> decreases readability for people that have spent time with Clojure.

It also points out why -> and ->> are not really about position anyway, it's about threading an expression. -> is to make Clojure read left-right instead of inside-out. ->> is to make Clojure read left-right when operating on sequences/collections.

Both are far, far more common and useful then being able to fill arguments anywhere.

David 

Nicolas Oury

unread,
Jul 6, 2010, 5:23:43 PM7/6/10
to clo...@googlegroups.com
Reading this thread, I realized how happy I was that I use a Lisp where anyone can create
 its own language construct with a new macro.

Greg, put that macro in a repository and in a jar on clojar, easily accessible from lein,
 and maybe people will use it. (I never had the need for something like this macro personally,
 but I believe you had, and Lisp allows a lot of different coding styles: a lot of people have asked for 
something like that)

Of course, a core library needs a lot of evaluations and thoughts before being changed.
So making it easily available to everybody that needs it is a good step, I think.

Best,

Nicolas.


Greg

unread,
Jul 6, 2010, 5:24:57 PM7/6/10
to clo...@googlegroups.com
> This would be most likely java interop, ie. ->.
> There the main arguments are 99% of the times the first or the last ones. So -> or ->> will work

OK, so what happens when one of the functions takes it in the front, and the other in the back?

Or what happens when you're using a piece of code that doesn't follow either convention? Are you saying such code doesn't exist?

In both those cases, -> and ->> become useless.

- Greg

Greg

unread,
Jul 6, 2010, 5:32:55 PM7/6/10
to clo...@googlegroups.com
Thanks for the reply Nicolas!

I might do that if I have more to offer than a single macro, as of now I think it'd be weird to create a clojar just for that.

It's easily accessible for anyone who searches the list though (or thinks of it themselves, as others have done). :-)

The code is pretty simple (copied from earlier):

(defmacro -->
([x] x)
([x form]
(if (seq? form)
`(let [~'_ ~x] ~form)
(list form x)
)
)
([x form & more]
; cache the result of the first form
(let [_ `(--> ~x ~form)]
`(--> ~_ ~@more)
)
)
)

And I kinda like how it looks like a bunch of those asian-style smiley faces. :-)

- Greg

Scott Jaderholm

unread,
Jul 6, 2010, 5:37:18 PM7/6/10
to clo...@googlegroups.com

On Tue, Jul 6, 2010 at 3:24 PM, Greg <gr...@kinostudios.com> wrote:
> This would be most likely java interop, ie. ->.
> There the main arguments are 99% of the times the first or the last ones. So -> or ->> will work

OK, so what happens when one of the functions takes it in the front, and the other in the back?

Or what happens when you're using a piece of code that doesn't follow either convention? Are you saying such code doesn't exist?

In both those cases, -> and ->> become useless.


You can use -> and ->> together to handle changing argument orders.

(-> "foo"
    (str "1")
    (->> (conj #{})))

Scott

(I would like --> or let-> in contrib though)

Scott Jaderholm

unread,
Jul 6, 2010, 5:39:37 PM7/6/10
to clo...@googlegroups.com
Sorry, this was just in response to "what happens when one of the functions takes it in the front, and the other in the back?" not "what happens when you're using a piece of code that doesn't follow either convention?", which would have to use #().

Meikel Brandmeyer

unread,
Jul 6, 2010, 5:42:55 PM7/6/10
to clo...@googlegroups.com
Hi,

Am 06.07.2010 um 23:24 schrieb Greg:

>> This would be most likely java interop, ie. ->.
>> There the main arguments are 99% of the times the first or the last ones. So -> or ->> will work
>
> OK, so what happens when one of the functions takes it in the front, and the other in the back?

This happens: http://kotka.de/blog/2010/04/Did_you_know_II.html.

> Or what happens when you're using a piece of code that doesn't follow either convention? Are you saying such code doesn't exist?

No. Such code exists. But the question is how often you encounter it. Core should care for the "usual" case and encourage people to stay there with their APIs.

I think, Nicolas made the most constructive statements in this thread so far.

Sincerely
Meikel

Paul Moore

unread,
Jul 6, 2010, 5:36:25 PM7/6/10
to clo...@googlegroups.com
On 6 July 2010 22:02, Greg <gr...@kinostudios.com> wrote:
> Greg you're enthusiasm is appreciated. But this ML is filled with talented
> and smart people who have an equal grasp of logic and reason who have been
> using Clojure for a couple years now and they aren't clamoring to your nice
> snippet of code. That's something to consider.
>
> It is indeed. I would be surprised though if I was the only person who sees
> --> as useful, and that brings us to your next sentence...
>
> It is interesting that this is not the first time such a feature has been
> requested. But again, I haven't ever had a need for such a general threading
> macro.
>
> I'll make a list here of the reasons given for Yay/Nay so far:
> Nay:
> 1) "I haven't had a need for a general threading macro."
> 2) The response so far is negative (and consists of repeating point #1
> above).

Sigh. I just *know* I shouldn't get involved in this...

- It isn't particularly readable. (I know you disagree with this, but
nevertheless, it has been stated as a negative point).
- It is perfectly possible to write the macro yourself and use it in
your own code. The fact that people haven't done this implies that
it's less useful than you claim (you'll say I'm repeating (1) here,
but never mind).
- It is easy to emulate using the existing macros and anonymous
functions like #(fn a % b).
- The named variable can be omitted from a form. The implications of
this are far from intuitive (and in the previous thread, there was
even a suggestion that this should be treated as an error).

> Yay:
> 1) This has been requested multiple times. (Which pretty much cancels out
> Nay #1 & #2).

I think the statement was that it's been requested twice. Better than
just once, but hardly "multiple"/

> 2) --> is a generalized version of -> and ->>, and therefore useful in
> situations where you can't use -> or ->>. It is the 'nth' to 'first' &
> 'last'.

If you use anonymous functions, there are no such situations. It's a
convenience rather than a generalisation.

> 3) It makes code more readable by explicitly indicating where the argument
> is passed.

Your view. I find it very *un*readable. The first time I saw the let->
proposal, the named argument completely confused me. First, I misread
the forms as expressions rather than templates, then I didn't follow
the way the variable was (in effect) updated in each step.

> At least if I've outlined the positions correctly, it's pretty sad that this
> is being fought against so hard.

You haven't.

> I'll make the additional points that:
> - There are plenty of macros and functions in the core that I'm sure you've
> never used once, yet they are there for others who find them useful. So
> simply responding to my proposal by saying, "I don't think I need it" isn't
> a valid reason against its inclusion.

Correct. The argument you are mischaracterising is that there is no
evidence that an existing body of code (such as clojure core or
clojure contrib) would benefit in any significant way from the
proposal.

Paul.

Paul Moore

unread,
Jul 6, 2010, 5:37:29 PM7/6/10
to clo...@googlegroups.com
On 6 July 2010 22:24, Greg <gr...@kinostudios.com> wrote:
>> This would be most likely java interop, ie. ->.
>> There the main arguments are 99% of the times the first or the last ones. So -> or ->> will work
>
> OK, so what happens when one of the functions takes it in the front, and the other in the back?
>
> Or what happens when you're using a piece of code that doesn't follow either convention? Are you
> saying such code doesn't exist?

Use an anonymous function.

> In both those cases, -> and ->> become useless.

Not useless, merely less convenient.
Paul.

Rich Hickey

unread,
Jul 6, 2010, 6:17:26 PM7/6/10
to clo...@googlegroups.com

On Jul 6, 2010, at 5:24 PM, Greg wrote:

>> This would be most likely java interop, ie. ->.
>> There the main arguments are 99% of the times the first or the last
>> ones. So -> or ->> will work
>
> OK, so what happens when one of the functions takes it in the front,
> and the other in the back?
>
> Or what happens when you're using a piece of code that doesn't
> follow either convention? Are you saying such code doesn't exist?
>
> In both those cases, -> and ->> become useless.
>

They simply don't apply. It's not as if nails make screwdrivers useless.

As others have said, this has come up before and been rejected. You'd
do well to heed what other people are saying. As a Lisp newcomer (your
trailing parens give you away), you might presume they have more
experience (though that doesn't make them inherently right).

Your assumption that because it has been asked for before it is
therefore needed is not correct. People ask for things all the time
that they don't need. Often people suggest things as a thought
exercise: "what's the generalization of -> ?"

-> and ->> are simplifications for consistent usages (target value and
sequences respectively). Many functions follow those conventions. When
they don't, or the operations are mixed, it's time to be more
explicit, not to come up with a trickier shorthand.

The bottom line is that --> and let-> are not good ideas, because they
mess up lexical naming badly. Setting aside that _ is idiomatic for
"don't care", consider the counterintuitive behavior of:

(--> 3 (prn _) (prn _))

At a glance, people will presume that prints the same thing twice.
The name _ is taking on different values with no intervening binding
form or recursion. And anything like let-> that allowed you to name
the arg has the same problem. There is no good name, as the semantics
of the parameter often change with each invocation. I.e. in your
original example:

(--> 3 (+ 1 _ 4) (prn "answer:" _))

_ is an input, then a result. Why give it the same name?

(--> 3 (+ 1 _ 4) (prn "answer:" _)) is not better than:

(let [x 3 a (+ 1 x 4)] (prn "answer:" a))

and in the latter case you'd never give x and a the same name, never
mind _.

In short, this and its variants are not going to get into Clojure.
Please be courteous and let it rest.

Thanks,

Rich

Greg

unread,
Jul 6, 2010, 6:36:14 PM7/6/10
to clo...@googlegroups.com
> As a Lisp newcomer (your trailing parens give you away)

I'm a newcomer to Clojure, not Lisp. I trail my parens because I think it results in more readable code, and I'm pretty sure I have good reasons for thinking so. They will continue to be trailed, and don't bother trying to suggest otherwise, unless you're interested in hearing me convince you of how wrong you are. :-)

> At a glance, people will presume that prints the same thing twice

> [..]

> _ is an input, then a result. Why give it the same name?

Now *that's* a reasonable and rational argument against including -->. :-)

I will point out though, that if you were to just read the code for -> or ->>, they would also seem counterintuitive until you read the documentation. They do not follow any standard Lisp calling convention, it is something that must be understood by reading the docs, and therefore the same applies to -->. Anyone encountering it would have to read the docs to understand it.

Once they read it, I think it becomes intuitive. Again, even The Joy of Clojure points out that people use commas with -> and ->> to indicate the location of the parameter. This is no different, except it actually works.

> In short, this and its variants are not going to get into Clojure

:-\

Thanks for making that clear though, as now people can point others to response when it comes up again.

> Please be courteous and let it rest.

That's my plan, and I think I've done my best to give the same level of courtesy to others as that I've received.

- Greg

Greg

unread,
Jul 6, 2010, 7:28:14 PM7/6/10
to clo...@googlegroups.com
> Now *that's* a reasonable and rational argument against including -->. :-)

On second thought, I don't think it is.

I know I said I'd let it rest, and I *am* letting it rest in that I've resigned myself to the understanding that you've made your decision and don't have plans on changing it. But I made a mistake in sounding like I agreed with your arguments, so let me know just correct that (you don't have to reply or even read this).

>> At a glance, people will presume that prints the same thing twice


At a glance, people will not have any correct idea of what most Lisp code does without reading the documentation, and this applies doubly-so for macros. Look at either the -> or ->> forms, or templates, etc, and you could make the exact same statement about how incorrect someone's first impression would be about them.

For example, here's something from core.clj that uses ->

(-> bvec (conj gvec) (conj val))

"At a glance", people will presume a function called -> is being called, with the arguments bvec, (conj gvec), and (conj val).

So, in other words, if you're actually serious about this argument, then you should do away with -> and ->> because the same applies to them. They're not intuitive "at a glance."

>> _ is an input, then a result. Why give it the same name?

No, _ is an input in all cases. It's the input from the form preceding it. You also have the same situation with -> and ->>.

So, it's a shame --> won't be included in the core, but I don't think it's a tragedy. :-p

- Greg

Michał Marczyk

unread,
Jul 6, 2010, 8:02:24 PM7/6/10
to clo...@googlegroups.com
On 7 July 2010 00:36, Greg <gr...@kinostudios.com> wrote:
> Again, even The Joy of Clojure points out that people use commas with -> and ->> to indicate the location of the parameter.

I've yet to see someone actually doing this (and I'm including the
Authors here).

Sincerely,
Michał

Rich Hickey

unread,
Jul 6, 2010, 8:11:09 PM7/6/10
to clo...@googlegroups.com

On Jul 6, 2010, at 8:02 PM, Michał Marczyk wrote:

> On 7 July 2010 00:36, Greg <gr...@kinostudios.com> wrote:
>> Again, even The Joy of Clojure points out that people use commas
>> with -> and ->> to indicate the location of the parameter.
>

I think that is a terrible practice and should be left out of the book.

Rich


Fogus

unread,
Jul 6, 2010, 8:13:31 PM7/6/10
to Clojure
> Once they read it, I think it becomes intuitive. Again, even The Joy
> of Clojure points out that people use commas with -> and ->> to
> indicate the location of the parameter. This is no different, except it

Well "The Joy of Clojure" is clearly wrong! Actually, the latest
version says "You can use the placement of commas as visual markers
for the stitch point" and a footnote that says "Since commas are
considered whitespace. The use here is instructive and not meant as
idiomatic."

The use of the commas helped it to click for me a long time ago, but
it's use has not persisted beyond the first few times using it.

Fogus

unread,
Jul 6, 2010, 8:29:17 PM7/6/10
to Clojure
> I think that is a terrible practice and should be left out of the book.

Sold! It gives us enough room to put the infix example back in. :O

;-)

:f

Greg

unread,
Jul 6, 2010, 8:26:56 PM7/6/10
to clo...@googlegroups.com
> The use of the commas helped it to click for me a long time ago

And I don't think you should be ashamed to admit that.

The threading macros are not intuitive, and the comma trick *is* useful to help in learning them.

Rich said:
> I think that is a terrible practice and should be left out of the book.

Really, "terrible"?

Fogus and I both found it helpful for learning the threading macros. tJoC doesn't recommend that it be used in practice, but its advice as a helpful learning tool, or "training wheels" if you will, is not only prudent, but shows that people find the placeholder syntax of --> to be intuitive.

- Greg

Greg

unread,
Jul 6, 2010, 8:45:27 PM7/6/10
to clo...@googlegroups.com
I think I've gotten too involved in this not-very-important debate for my own good. :-D

So just FYI, this will be my last response. If you want a reply from me please contact me off-list.

If I offended anyone, you have my apologies.

May the force be with you,

- Greg

Fogus

unread,
Jul 6, 2010, 9:59:10 PM7/6/10
to Clojure
> And I don't think you should be ashamed to admit that.

That's a relief! ;-)

> helpful learning tool, or "training wheels" if you will, is not only
> prudent, but shows that people find the placeholder syntax
> of --> to be intuitive.

I should say that while I do think that the ,,, trick could be
instructive, that sentiment is very far from advocating -->, -$>, or
let->.

aria42

unread,
Jul 7, 2010, 8:52:27 AM7/7/10
to Clojure
I've needed --> a few times in my code. I don't think I need it as
much as just -> or ->>. Most of the time I've needed it is because I
or someone else essentially had parameters
in the wrong order. Maybe it belongs in contrib along with -?> which
I've needed sparingly as well, but have found useful and would've been
non-trivial to have conjured.

On Jul 6, 5:22 pm, David Nolen <dnolen.li...@gmail.com> wrote:

Laurent PETIT

unread,
Jul 7, 2010, 9:02:09 AM7/7/10
to clo...@googlegroups.com
2010/7/7 aria42 <ari...@gmail.com>:

> I've needed --> a few times in my code. I don't think I need it as
> much as just -> or ->>. Most of the time I've needed it is because I
> or someone else essentially had parameters
> in the wrong order. Maybe it belongs in contrib along with -?> which
> I've needed sparingly as well, but have found useful and would've been
> non-trivial to have conjured.

-?> is particularly suitable when doing lots of java interop. I use it
quite a lot in my own code in counterclockwise, for example.

Greg

unread,
Jul 13, 2013, 8:13:35 PM7/13/13
to clo...@googlegroups.com
This email just in case any Googler's stumble across this thread searching for a generalized threading macro (and a happy conclusion to it). Googling for examples on this macro was very difficult in my experience, so maybe this might help someone.

As I found out via the "The case for as->> ("as-last")" thread, a generalized threading macro was apparently introduced in Clojure 1.5: the as-> form:

clojure.core/as->
([expr name & forms])
Macro
  Binds name to expr, evaluates the first form in the lexical context
  of that binding, then binds name to that result, repeating for each
  successive form, returning the result of the last form.

As this tweet demonstrates, here's how it looks:

(as-> "/tmp" x (java.io.File. x) (file-seq x) (filter (memfn isDirectory) x) (count x))

First 'x' is bound to "/tmp" and a file is made out of it. 'x' is rebound again to the resulting file and a put through the 'file-seq' function, etc.

- Greg

--
Please do not email me anything that you are not comfortable also sharing with the NSA.

Daniel Dinnyes

unread,
Jul 13, 2013, 9:08:08 PM7/13/13
to clo...@googlegroups.com
Hiya,


There the main arguments are 99% of the times the first or the last ones. So -> or ->> will work.

Just made a quick search on `main arguments` on both Google and Wikipedia. Do you mean the arguments in `public static void main (String[] args)`? If not please provide some definition what do you mean by main arguments. Else the point is meaningless.

Cheers,

Daniel

Daniel Dinnyes

unread,
Jul 13, 2013, 10:49:01 PM7/13/13
to clo...@googlegroups.com, gr...@kinostudios.com
Hey guys, I just came up with some good shit, check this out!

(defmacro ->>>
  ([x] x)
  ([x alias form]
     `(let [~alias ~x] ~form))
  ([x alias form & more]
     (let [y `(->>> ~x ~alias ~form)]
       `(->>> ~y ~@more))))


(->>> "test-string-with-lots-of-dashes"
        x (s/split x #"-")
        y (interleave y (range))
        z (s/join #"_" z)
        z (s/join " * " ["I am serious" z "Not Kidding!!" z]))

> "I am serious * test_0_string_1_with_2_lots_3_of_4_dashes_5 * Not Kidding!! * test_0_string_1_with_2_lots_3_of_4_dashes_5"


So... when are we including it in the core?

Cheers,
D.

Jeremy Heiler

unread,
Jul 14, 2013, 12:34:02 PM7/14/13
to clo...@googlegroups.com
On Sat, Jul 13, 2013 at 9:08 PM, Daniel Dinnyes <dinn...@gmail.com> wrote:
> Just made a quick search on `main arguments` on both Google and Wikipedia.
> Do you mean the arguments in `public static void main (String[] args)`? If
> not please provide some definition what do you mean by main arguments. Else
> the point is meaningless.

He means the arguments you are threading.

Jeremy Heiler

unread,
Jul 14, 2013, 12:39:18 PM7/14/13
to clo...@googlegroups.com
On Sat, Jul 13, 2013 at 10:49 PM, Daniel Dinnyes <dinn...@gmail.com> wrote:
> (->>> "test-string-with-lots-of-dashes"
> x (s/split x #"-")
> y (interleave y (range))
> z (s/join #"_" z)
> z (s/join " * " ["I am serious" z "Not Kidding!!" z]))

Why not use as-> or a let in this situation?

Daniel Dinnyes

unread,
Jul 15, 2013, 6:30:24 PM7/15/13
to clo...@googlegroups.com
Hmm, good point, especially the `let` one... What is `as->`? I can't find anything about that.

There is one benefit over `let` though: it is more explicit. Let allows you to define independent bindings mixed together with multiple threads of dependent bindings (which can be mixed in random combinations). As the number of bindings increase it becomes quite messy, and hard to decipher which line depends on which. I have seen, even written myself (shame on me), such code. I feel that this is the main reason for the `core` threading macros too ("why not use let instead?" would still apply then). On the other hand as my simple example code demonstrates (off the top of my hat, cuz ya need to show da code!), in a functional language the parameter order shouldn't matter, and there shouldn't be privileged (main!?) parameter positions (Clojure is the landguage of multimethods after all!)

(BTW, have you noticed you can do destructuring with ->>> out of the box ;)

Anyway, I see the reason for -> and ->> macros and indeed the first and last positions are special in some sense. The -> is good for navigating protocols, and ->> is good for functions expected/designed to be partially applied. Is that correct?

Cheers,

Daniel

Jeremy Heiler

unread,
Jul 15, 2013, 6:53:09 PM7/15/13
to clo...@googlegroups.com

On July 15, 2013 at 6:30:28 PM, Daniel Dinnyes (dinn...@gmail.com) wrote:

Hmm, good point, especially the `let` one... What is `as->`? I can't find anything about that.
There is one benefit over `let` though: it is more explicit. Let allows you to define independent bindings mixed together with multiple threads of dependent bindings (which can be mixed in random combinations). As the number of bindings increase it becomes quite messy, and hard to decipher which line depends on which. I have seen, even written myself (shame on me), such code. I feel that this is the main reason for the `core` threading macros too ("why not use let instead?" would still apply then). On the other hand as my simple example code demonstrates (off the top of my hat, cuz ya need to show da code!), in a functional language the parameter order shouldn't matter, and there shouldn't be privileged (main!?) parameter positions (Clojure is the landguage of multimethods after all!)

Your ->>> is a bit awkward because the symbol to the left represents the value of the previous expression, not the value of the following expression as is the case with most "bindings forms" in Clojure. Also, as-> simplifies your use case by only needing to identify one name that is used in all the threaded forms. Honestly, if I'm going to do anything more complicated that as->, I would rethink how I want to express my code.

Anyway, I see the reason for -> and ->> macros and indeed the first and last positions are special in some sense. The -> is good for navigating protocols, and ->> is good for functions expected/designed to be partially applied. Is that correct?

The threading macros operate on the forms directly, so I'm not sure what you mean by "partially applied" here. The big win for ->> is that the sequence functions in Clojure core expect the sequence to be last. This is handy for threading a sequence through multiple transformations.

Gary Johnson

unread,
Jul 16, 2013, 11:07:53 PM7/16/13
to clo...@googlegroups.com
Ugh. What a pointless thread. Someone could have just said:

 ---
 It's already in clojure 1.5. The form you are looking for is called as->.
 Your original example would be written like this:

  (as-> 3 x (+ 1 x 4) (prn "answer:" x))
  ---

Done. Yeesh.

Alexander Yakushev

unread,
Jul 17, 2013, 12:49:44 PM7/17/13
to clo...@googlegroups.com
What a twist.

Does any of the participants care to comment on this one? A hundred posts of bashing a person from the position of authority while the macro in question already sits in Core. I am against the usage of it myself, and closely followed previous discussions on this topic to understand the arguments being brought there; but arguing against something you already accepted is beyond my comprehension, tbh.

Nelson Morris

unread,
Jul 17, 2013, 12:55:37 PM7/17/13
to Clojure
Note the original discussion was from 2010.


--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Alexander Yakushev

unread,
Jul 17, 2013, 1:03:14 PM7/17/13
to clo...@googlegroups.com
I'm sorry for jumping in then. Should have paid more attention to the dates.

Nevertheless I wonder why while standing on such a solid ground, the Core team eventually gave in. I could see a use for cond->, but as-> is basically the same macro that was suggested in topics like this, and was rejected by Rich and company. Have anything changed in Clojure since then, that arguments against as-> are no longer valid?

David Nolen

unread,
Jul 17, 2013, 1:03:41 PM7/17/13
to clojure
While the macro can do what the original enhancement request suggested that's not the actual problem the new threading macros were intended to solve. They were primarily added to eliminate:

(let [x ...
      x ...
      x ...]
   ...)

Which is pretty ugly and also it's pretty easy to get into trouble.

David


--

Steven Degutis

unread,
Jul 17, 2013, 1:15:16 PM7/17/13
to clo...@googlegroups.com
In that case, shouldn't it be named let-> instead of as->?

Alexander Yakushev

unread,
Jul 17, 2013, 1:20:40 PM7/17/13
to clo...@googlegroups.com
Thank you for the explanation, it makes sense to me now.

Sean Corfield

unread,
Jul 17, 2013, 9:50:17 PM7/17/13
to clo...@googlegroups.com
See this discussion: https://groups.google.com/forum/#!topic/clojure/67JQ7xSUOM4

(kinda hard to Google for functions with -> in their name so it took
me a bit of digging!)

Sean
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)

Daniel Dinnyes

unread,
Jul 19, 2013, 10:18:15 AM7/19/13
to clo...@googlegroups.com


On Monday, July 15, 2013 11:53:09 PM UTC+1, Jeremy Heiler wrote:

On July 15, 2013 at 6:30:28 PM, Daniel Dinnyes (dinn...@gmail.com) wrote:

Hmm, good point, especially the `let` one... What is `as->`? I can't find anything about that.

http://clojure.github.io/clojure/clojure.core-api.html#clojure.core/as-%3E

There is one benefit over `let` though: it is more explicit. Let allows you to define independent bindings mixed together with multiple threads of dependent bindings (which can be mixed in random combinations). As the number of bindings increase it becomes quite messy, and hard to decipher which line depends on which. I have seen, even written myself (shame on me), such code. I feel that this is the main reason for the `core` threading macros too ("why not use let instead?" would still apply then). On the other hand as my simple example code demonstrates (off the top of my hat, cuz ya need to show da code!), in a functional language the parameter order shouldn't matter, and there shouldn't be privileged (main!?) parameter positions (Clojure is the landguage of multimethods after all!)

Your ->>> is a bit awkward because the symbol to the left represents the value of the previous expression, not the value of the following expression as is the case with most "bindings forms" in Clojure. Also, as-> simplifies your use case by only needing to identify one name that is used in all the threaded forms. Honestly, if I'm going to do anything more complicated that as->, I would rethink how I want to express my code.

 You are trying to pick it up on the wrong end. Check this out:

(-> "test-string-with-lots-of-dashes"
    (fn [x] (s/split x #"-"))
    (fn [y] (interleave y (range)))
    (fn [z] (s/join #"_" z))
    (fn [z] (s/join " * " ["I am serious" z "Not Kidding!!" z])))


The above code is not valid, as the -> macro sees the function declarations as lists, and tries to thread the argument through it. It is quite intuitive though IMHO. Now with that in mind, check this baby once more:

(->>> "test-string-with-lots-of-dashes"
        x (s/split x #"-")
        y (interleave y (map count y))
        z (apply assoc {} z)
        {:strs [test string with lots of dashes]}
        (s/join " " ["test is" test ";" "string is" string ";" "with is" with ";" "lots is" lots ";" "of is" of ";" "dashes is" dashes ";" "TOTAL:" (+ test string with lots of dashes)]))


> "test is 4 ; string is 6 ; with is 4 ; lots is 4 ; of is 2 ; dashes is 6 ; TOTAL: 26"


Yee-haw !!!

Anyway, I see the reason for -> and ->> macros and indeed the first and last positions are special in some sense. The -> is good for navigating protocols, and ->> is good for functions expected/designed to be partially applied. Is that correct?

The threading macros operate on the forms directly, so I'm not sure what you mean by "partially applied" here. The big win for ->> is that the sequence functions in Clojure core expect the sequence to be last. This is handy for threading a sequence through multiple transformations.

By partially applied I mean using the `partial` function on them to partially apply all parameters except the last few. If a function can be partially applied so that only the last parameter is not applied, where it could accept the argument in question, then it would work well with ->> macro.

(let [myfun (partial apply assoc {})]
  (->> ["test" "string" "with" "lots" "of" "dashes"]
       myfun))

(->> ["test" "string" "with" "lots" "of" "dashes"]
     (apply assoc {}))


Hope that makes it clearer what I meant.

Regards,

Daniel

Daniel Dinnyes

unread,
Jul 19, 2013, 11:28:44 AM7/19/13