They're called "thrush" but many of us pronounce them "thread" and "thread end".
(-> x expr1 expr2 ... exprN) evaluates x, then takes the result and puts it as the first argument in expr1, evaluates that, and puts the result in the first argument of expr2, repeating until exprN, where the result is returned.
(->> x expr1 expr2 exprN) does the same thing, but instead of making it the first argument it makes it the last argument of subsequent expressions.
Clojure's culture generally agrees that the "subject" of function calls should be the first argument, so in general the -> operator is all you need. Sometimes you're shuttling data around, and then ->> comes into play.
Please note that multi-ping in this is atypical and constructed that way for didactic purposes (i.e., to be transformed later). Usually you'd see it as:
(defn multi-ping [] (apply str (repeat 16 ping)))
For readability, some people might prefer to use a let. I think with only 2 levels of nesting, it should be pretty easy to see what's up and I wouldn't bother, but that'd look like:
(-> x expr1 expr2 ... exprN) evaluates x, then takes the result and puts it as the first argument in expr1, evaluates that, and puts the result in the first argument of expr2, repeating until exprN, where the result is returned.
(->> x expr1 expr2 exprN) does the same thing, but instead of making it the first argument it makes it the last argument of subsequent expressions.
Clojure's culture generally agrees that the "subject" of function calls should be the first argument, so in general the -> operator is all you need. Sometimes you're shuttling data around, and then ->> comes into play.
Please note that multi-ping in this is atypical and constructed that way for didactic purposes (i.e., to be transformed later). Usually you'd see it as:
For readability, some people might prefer to use a let. I think with only 2 levels of nesting, it should be pretty easy to see what's up and I wouldn't bother, but that'd look like: