Threading operator generalisation

33 views
Skip to first unread message

Andreas Kostler

unread,
Jul 18, 2011, 8:10:57 PM7/18/11
to clo...@googlegroups.com
Maybe you might find this useful

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

This allows for a more flexible threading operation:

user> (-> "c"
(third-param "a" "b" :? "d")
println)
first a second b third c fourth d
nil

Cheers
Andreas


Sean Corfield

unread,
Jul 18, 2011, 8:33:57 PM7/18/11
to clo...@googlegroups.com
Requests for a "more general" threading macro are usually met with a
link to the thread where Rich says this is not needed and we should
not encourage functions that need threading anywhere except the first
or last position. I don't have the link handy (sorry).

The usual objection centers around the fact that whatever symbol is
used for the binding position then becomes a "varying" thing within
the expression:

(-newthread-> "x"
(str "y" :? "z")
(str "a" :? "b")
println)

Here :? means two different things which seems counter to how Clojure
works elsewhere...

Sean

Andreas Kostler

unread,
Jul 18, 2011, 8:41:29 PM7/18/11
to clo...@googlegroups.com
Ups, I'm not aware of that thread...I just found a more general threading operator handy sometimes.
I do kinda agree that we shouldn't necessarily encourage threading in arbitrary positions.
However, I can't quite follow your second argument.
While it does mean two different things, I don't see the problem with that.
We could use _ instead of :? to indicate we don't care about the 'meaning'.

Anyway, I'll delete this evil hack from my repository ;)

> --
> 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

Sean Corfield

unread,
Jul 18, 2011, 9:47:04 PM7/18/11
to clo...@googlegroups.com
On Mon, Jul 18, 2011 at 5:41 PM, Andreas Kostler
<andreas.koe...@gmail.com> wrote:
> Ups, I'm not aware of that thread...I just found a more general threading operator handy sometimes.
> I do kinda agree that we shouldn't necessarily encourage threading in arbitrary positions.
> However, I can't quite follow your second argument.
> While it does mean two different things, I don't see the problem with that.
> We could use _ instead of :? to indicate we don't care about the 'meaning'.

But _ means "don't care" by convention in Clojure, for values that are
ignored...

The issue is about bindings and immutability: foo should mean the same
value throughout an expression - that's kind of fundamental to Clojure
(IMO). With your macro (and the previous examples introduced by
others), some arbitrary symbol changes its meaning in each consecutive
form without any marker for lexical or dynamic binding... so it's
behaving like a mutable iterator or loop variable :(

With only a couple more characters you can already do what you need
without a new macro:

(-> "x"
(#(str "y" % "z"))
(#(str "a" % "b"))
println)

There's also the possibility of mixing -> and ->> to splice in first
arg / last arg threading. There was a great blog post about this
recently but I can't find it (-> and ->> are remarkably hard to search
for on Google!)...
--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/
Railo Technologies, Inc. -- http://www.getrailo.com/

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

Andreas Kostler

unread,
Jul 18, 2011, 9:51:24 PM7/18/11
to clo...@googlegroups.com

On 19/07/2011, at 11:47 AM, Sean Corfield wrote:

> On Mon, Jul 18, 2011 at 5:41 PM, Andreas Kostler
> <andreas.koe...@gmail.com> wrote:
>> Ups, I'm not aware of that thread...I just found a more general threading operator handy sometimes.
>> I do kinda agree that we shouldn't necessarily encourage threading in arbitrary positions.
>> However, I can't quite follow your second argument.
>> While it does mean two different things, I don't see the problem with that.
>> We could use _ instead of :? to indicate we don't care about the 'meaning'.
>
> But _ means "don't care" by convention in Clojure, for values that are
> ignored...
>
> The issue is about bindings and immutability: foo should mean the same
> value throughout an expression - that's kind of fundamental to Clojure
> (IMO). With your macro (and the previous examples introduced by
> others), some arbitrary symbol changes its meaning in each consecutive
> form without any marker for lexical or dynamic binding... so it's
> behaving like a mutable iterator or loop variable :(

I agree with that, however, :? or _ or whatever doesn't bind to anything. It merely gets replaced.
-> already does this in a hidden way...e.g. there's no symbol but the meaning of 'first argument' changes
in each consecutive form.

>
> With only a couple more characters you can already do what you need
> without a new macro:
>
> (-> "x"
> (#(str "y" % "z"))
> (#(str "a" % "b"))
> println)

Here, the meaning of % changes?!?

>
> There's also the possibility of mixing -> and ->> to splice in first
> arg / last arg threading. There was a great blog post about this
> recently but I can't find it (-> and ->> are remarkably hard to search
> for on Google!)...

I agree :)

> --
> Sean A Corfield -- (904) 302-SEAN
> An Architect's View -- http://corfield.org/
> World Singles, LLC. -- http://worldsingles.com/
> Railo Technologies, Inc. -- http://www.getrailo.com/
>
> "Perfection is the enemy of the good."
> -- Gustave Flaubert, French realist novelist (1821-1880)
>

Chas Emerick

unread,
Jul 18, 2011, 10:04:22 PM7/18/11
to clo...@googlegroups.com
Just as a general comment: just because any of our idiosyncratic functions and macros aren't accepted for inclusion in Clojure proper does _not_ mean that they can't find a full and vigorous life in their own libraries. Clojure is readily extended and improved without adding bits to the language itself, and Clojure development benefits from a very capable dependency management infrastructure; take advantage of it!

In this particular case, I'll bet that someone has an arbitrary threading macro in a library somewhere you can use…or, you can create such a library. Given the number of times this has come up, I'm certain it would find some love.

- Chas

Ken Wesson

unread,
Jul 18, 2011, 10:19:06 PM7/18/11
to clo...@googlegroups.com
On Mon, Jul 18, 2011 at 10:04 PM, Chas Emerick <ceme...@snowtide.com> wrote:
> Just as a general comment: just because any of our idiosyncratic functions and macros aren't accepted for inclusion in Clojure proper does _not_ mean that they can't find a full and vigorous life in their own libraries.  Clojure is readily extended and improved without adding bits to the language itself, and Clojure development benefits from a very capable dependency management infrastructure; take advantage of it!
>
> In this particular case, I'll bet that someone has an arbitrary threading macro in a library somewhere you can use…or, you can create such a library.  Given the number of times this has come up, I'm certain it would find some love.

Given the number of times this has come up, I'd say it's evidence that
something similar really should be in core -- just this once.

I'd go for one that uses a normal symbol, e.g. (-x> a (expr1) (foo a
3) (bar :k a 7) ...) where the symbol to be bound is explicitly
provided by the user.

It isn't desired that often, since collections tend to be the last
argument and maps tend to be the first, but there are some cases where
one changes type partway through (e.g. works on a collection, then
extracts an element and works on that) or a function takes an argument
in an awkward place (e.g. nth takes the collection first, not last).
And as we've noticed, it keeps getting reinvented regularly by new
people.

I actually rarely use any of the threading macros myself, mind.

--
Protege: What is this seething mass of parentheses?!
Master: Your father's Lisp REPL. This is the language of a true
hacker. Not as clumsy or random as C++; a language for a more
civilized age.

Sean Corfield

unread,
Jul 19, 2011, 12:38:59 AM7/19/11
to clo...@googlegroups.com
On Mon, Jul 18, 2011 at 6:51 PM, Andreas Kostler
<andreas.koe...@gmail.com> wrote:
>> (-> "x"
>>    (#(str "y" % "z"))
>>    (#(str "a" % "b"))
>>    println)
> Here, the meaning of % changes?!?

Not really, each #() is a scope for % as if it read:

(-> "x"
((fn [x] (str "y" x "z")))
((fn [c] (str "a" c "b"))
println)

Andreas Kostler

unread,
Jul 19, 2011, 12:42:09 AM7/19/11
to clo...@googlegroups.com
Oh yeah, fair enough.
I'm not convinced though. I don't see :? changing as such, as well as I think the syntactical representation below is just as confusing...
I'll rest this case :) It's not all that useful anyway.

Sean Corfield

unread,
Jul 19, 2011, 12:55:40 AM7/19/11
to clo...@googlegroups.com
On Mon, Jul 18, 2011 at 7:04 PM, Chas Emerick <ceme...@snowtide.com> wrote:
> Just as a general comment: just because any of our idiosyncratic functions and macros aren't accepted for inclusion in Clojure proper does _not_ mean that they can't find a full and vigorous life in their own libraries.

Yeah, sorry, I kinda jumped on the negative instead of encouraging
this to go into a community library :(

> In this particular case, I'll bet that someone has an arbitrary threading macro in a library somewhere you can use…or, you can create such a library.  Given the number of times this has come up, I'm certain it would find some love.

This is the third(?) time so you are probably right. This might be a
good place to start:

https://github.com/pallet/thread-expr

Andreas Kostler

unread,
Jul 19, 2011, 12:57:47 AM7/19/11
to clo...@googlegroups.com
Hmm...I didn't get Chas' reply...Was that a private response? I sometimes feel I miss out on certain replies to the group.
Cheers
Andreas

Sean Corfield

unread,
Jul 19, 2011, 1:12:35 AM7/19/11
to clo...@googlegroups.com
On Mon, Jul 18, 2011 at 9:57 PM, Andreas Kostler
<andreas.koe...@gmail.com> wrote:
> Hmm...I didn't get Chas' reply...Was that a private response?

No, it probably just hasn't gotten to everyone yet...? I see it on the web:

http://groups.google.com/group/clojure/browse_thread/thread/949abab9c206dc1a

Andreas Kostler

unread,
Jul 19, 2011, 1:16:25 AM7/19/11
to clo...@googlegroups.com
I just realised I'm missing quite a few of the replies...including Kens.
I've gotta follow the web list more closely.
Andreas

Andreas Kostler

unread,
Jul 19, 2011, 1:19:18 AM7/19/11
to clo...@googlegroups.com
I've posted the source as a suggested extension to:
https://github.com/pallet/thread-expr

As far as I'm concerned, that's the end of it :)
I'm sorry for reposting this topic...I should have checked the mailing list history :(
Andreas

On 19/07/2011, at 3:12 PM, Sean Corfield wrote:

Sean Corfield

unread,
Jul 19, 2011, 1:26:13 AM7/19/11
to clo...@googlegroups.com
On Mon, Jul 18, 2011 at 10:19 PM, Andreas Kostler
<andreas.koe...@gmail.com> wrote:
> I've posted the source as a suggested extension to:
> https://github.com/pallet/thread-expr
> As far as I'm concerned, that's the end of it :)
> I'm sorry for reposting this topic...I should have checked the mailing list
> history :(

No, please don't apologize. I'm sorry for discouraging you :(

I really hope thread-expr picks this up...

Andreas Kostler

unread,
Jul 19, 2011, 1:30:14 AM7/19/11
to clo...@googlegroups.com
You're not discouraging...It's just mailing list 'etiquette' to check if some topic has been discussed before, I guess :)

Reply all
Reply to author
Forward
0 new messages