the -> macro and partial function

安稳与你 提交于 2020-01-16 03:28:07

问题


Why can't I use a partial function or an anonymous function in a chained call with ->?

(->
    "123"
    seq
    sort
    reverse
    (partial apply str))
=> #<core$partial$fn__4230 clojure.core$partial$fn__4230@335f63af>

I would have thought the partial function would be created and immediately applied to the previous function's result, but it's actually returned itself.

A simple chaining of functions with one parameter of course works without problems:

(->
    "123"
    seq
    sort
    reverse
    clojure.string/join)

回答1:


As -> and ->> are just macros, you can test what -> expands to with macroexpand-1 (note the quote ')

user=> (macroexpand-1 '(->
       "123"
       seq
       sort
       reverse
       (partial apply str)))

(partial (reverse (sort (seq "123"))) apply str)

So that's why you get a function instead of a string - result is partial of collection (which is a function) returned from reverse and apply str as arguments.

If for some reason you need to apply a funcion in ->, you should wrap it in parens:

user=> (macroexpand-1 '(->
    "123"
    ((partial sort))  ; we add parens around so -> have plase to insert
   ))
((partial sort) "123")

Or you can use ->> macro, as it is doing same work as partial here:

((partial str "hello ") "world")

"hello world"

and

(->> (str "hello ") "world")    ; expands to (str "hello " "world")

"hello world"



回答2:


The -> macro is defined as:

(doc ->)
-------------------------
clojure.core/->
([x & forms])
Macro
  Threads the expr through the forms. Inserts x as the
  second item in the first form, making a list of it if it is not a
  list already. If there are more forms, inserts the first form as the
  second item in second form, etc.

Therefore (apply str) yields an error, because the list from the former function is entered as the second argument:

(->
    "123"
    seq
    sort
    reverse
    (apply str))
ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.IFn  clojure.core/apply (core.clj:624)

Instead one should use the macro ->>, which applies to the last item:

clojure.core/->>
([x & forms])
Macro
  Threads the expr through the forms. Inserts x as the
  last item in the first form, making a list of it if it is not a
  list already. If there are more forms, inserts the first form as the
  last item in second form, etc.

Then the solution with apply looks like this:

(->>
  "123"
  seq
  sort
  reverse
  (apply str))
=> "321"

UPDATE: expanded version of the -> and ->>:

(macroexpand-1 '(->>
                 "123"
                 seq
                 sort
                 reverse
                 (apply str)))
=> (apply str (reverse (sort (seq "123"))))

As you can see from the expanded versions, there's only a difference in how the form (apply str) is interpreted. With -> the second item is inserted while ->> inserts the last item.

(macroexpand-1 '(->
                 "123"
                 seq
                 sort
                 reverse
                 (apply str)))
=> (apply (reverse (sort (seq "123"))) str)


来源:https://stackoverflow.com/questions/29188854/the-macro-and-partial-function

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!