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
> 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
Stu
> 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
In my experience, unneeded versatility == support headache.
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
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.
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
> (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
> 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.
>
On Jul 6, 2010, at 2:26 PM, Wilson MacGyver wrote:Yes, let's handicap ourselves and then disparage a useful macro as "unneeded."
> 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.
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
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 :)
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
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
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
>> 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.
Cheers,
Aaron
> 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
> > 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 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.
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
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).
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
> 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 workOK, 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.
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
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.
Use an anonymous function.
> In both those cases, -> and ->> become useless.
Not useless, merely less convenient.
Paul.
>> 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
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
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
I've yet to see someone actually doing this (and I'm including the
Authors here).
Sincerely,
Michał
> 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
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
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
-?> is particularly suitable when doing lots of java interop. I use it
quite a lot in my own code in counterclockwise, for example.
clojure.core/as->([expr name & forms])MacroBinds name to expr, evaluates the first form in the lexical contextof that binding, then binds name to that result, repeating for eachsuccessive form, returning the result of the last form.
There the main arguments are 99% of the times the first or the last ones. So -> or ->> will work.
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.
--
--
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.
--
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.
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.
On July 19, 2013 at 11:28:50 AM, Daniel Dinnyes (dinn...@gmail.com) wrote:
Oh, and the implementation is clean and simple. Just cleaned it up a bit from all the clutter:
;;; The DUH..IT'S SO SIMPLE! macro
(defmacro duh->
([x alias form]
`(let [~alias ~x] ~form))
([x alias form & more]
`(duh-> (duh-> ~x ~alias ~form) ~@more)))
This gives YOU the full power of destructuring and threading through functions with ANY parameter order. Full `let` power! Why not use let then? True, the only difference is the restriction to a single thread of bindings, not allowing for mixing things up. As David Nolen points out below though, that is a HUGE benefit, the reason why all the threading macros exist. Beside that restriction, this fine macro above won't struggle to take away any of the `let` goodies, unlike ->, ->>, as->, or anything.
I understood David's comment differently, that the current threading macros exist so that explicit bindings for each threaded form are not needed for they very specific cases they intend to simplify. I'm not saying your macro is dumb, I just don't find the sugar particularly tasty. ;-)
I understood David's comment differently, that the current threading macros exist so that explicit bindings for each threaded form are not needed for they very specific cases they intend to simplify. I'm not saying your macro is dumb, I just don't find the sugar particularly tasty. ;-)
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.