Clojure

RFC: Anonymous functions interaction with -> and ->> threading macros

Details

  • Type: Enhancement Enhancement
  • Status: Closed Closed
  • Priority: Minor Minor
  • Resolution: Declined
  • Affects Version/s: Release 1.6
  • Fix Version/s: None
  • Component/s: None
  • Labels:
    None

Description

This is more of a starting point for discussion than a feature request. It'd be easy to write and submit the patch, but I want to ask if it's a good idea, since it would alter the semantics of two core macros, '> and '>>; and if it is a good idea, what considerations need to be balanced.

For threading macros, I'd like to special-case forms that begin with 'fn and 'fn*. It's often useful (but maybe a bad idea; that's why I'd like to start the discussion) to use the threading macros in conjunction with anonymous functions in addition to forms, like so (contrived example):

(defn sigmoid [x] (-> x 
                      - 
                      Math/exp 
                      (+ 1) 
                      #(/ 1 %)))

This won't compile; #(/ 1 %) expands to the form

(fn* [p1__1389#] (/ 1 p1__1389#))

modulo gensym, of course. The threading macro, not treating fn* and fn specially, alters that to:

(fn* (clojure.core/-> (clojure.core/-> (clojure.core/-> x -) Math/exp) (+ 1)) [p1__1417#] (/ 1 p1__1417#))

which is a fn* with a non-symbol (illegal label) before its binding vector, raising an error.

Is this worth "fixing", or are the benefits to small to justify the added complexity of a special case in the ->, ->> threading macros?

Activity

Alex Miller made changes -
Field Original Value New Value
Description This is more of a starting point for discussion than a feature request. It'd be easy to write and submit the patch, but I want to ask if it's a good idea, since it would alter the semantics of two core macros, '-> and '->>; and if it is a good idea, what considerations need to be balanced.

For threading macros, I'd like to special-case forms that begin with 'fn and 'fn*. It's often useful (but maybe a bad idea; that's why I'd like to start the discussion) to use the threading macros in conjunction with anonymous functions in addition to forms, like so (contrived example):

(defn sigmoid [x] (-> x
                      -
                      Math/exp
                      (+ 1)
                      #(/ 1 %)))

This won't compile; #(/ 1 %) expands to the form

(fn* [p1__1389#] (/ 1 p1__1389#))

modulo gensym, of course. The threading macro, not treating fn* and fn specially, alters that to:

(fn* (clojure.core/-> (clojure.core/-> (clojure.core/-> x -) Math/exp) (+ 1)) [p1__1417#] (/ 1 p1__1417#)),

which is a fn* with a non-symbol (illegal label) before its binding vector, raising an error.

Is this worth "fixing", or are the benefits to small to justify the added complexity of a special case in the ->, ->> threading macros?
This is more of a starting point for discussion than a feature request. It'd be easy to write and submit the patch, but I want to ask if it's a good idea, since it would alter the semantics of two core macros, '-> and '->>; and if it is a good idea, what considerations need to be balanced.

For threading macros, I'd like to special-case forms that begin with 'fn and 'fn*. It's often useful (but maybe a bad idea; that's why I'd like to start the discussion) to use the threading macros in conjunction with anonymous functions in addition to forms, like so (contrived example):

{code}
(defn sigmoid [x] (-> x
                      -
                      Math/exp
                      (+ 1)
                      #(/ 1 %)))
{code}

This won't compile; {{#(/ 1 %)}} expands to the form

{code}
(fn* [p1__1389#] (/ 1 p1__1389#))
{code}

modulo gensym, of course. The threading macro, not treating fn* and fn specially, alters that to:

{code}
(fn* (clojure.core/-> (clojure.core/-> (clojure.core/-> x -) Math/exp) (+ 1)) [p1__1417#] (/ 1 p1__1417#))
{code}

which is a fn* with a non-symbol (illegal label) before its binding vector, raising an error.

Is this worth "fixing", or are the benefits to small to justify the added complexity of a special case in the ->, ->> threading macros?
Hide
Michael O. Church added a comment -

Thanks Alex!

Show
Michael O. Church added a comment - Thanks Alex!
Hide
Gary Fredericks added a comment -

Just one of the special cases you'd want to consider is when fn does not mean clojure.core/fn, which is certainly realistic.

Show
Gary Fredericks added a comment - Just one of the special cases you'd want to consider is when fn does not mean clojure.core/fn, which is certainly realistic.
Hide
Michael O. Church added a comment - - edited

Yes, I realize that.

Looking at the ticket again, I retract my support of this change.

Here's why: (1) that "fix" would alter the semantics of the threading macros, which would break existing code, and (2) there's a really easy way to get anonymous functions into threading macros: just surround the

#(...)
expression with an extra set of parentheses.

I retract my request for this feature. It's easy enough to do this: instead of,

(-> x f #(g h %))

it's this:

(-> x f (#(g h %)))

Saving 2 characters, like so, is not worth a breaking change IMO.

Show
Michael O. Church added a comment - - edited Yes, I realize that. Looking at the ticket again, I retract my support of this change. Here's why: (1) that "fix" would alter the semantics of the threading macros, which would break existing code, and (2) there's a really easy way to get anonymous functions into threading macros: just surround the
#(...)
expression with an extra set of parentheses. I retract my request for this feature. It's easy enough to do this: instead of,
(-> x f #(g h %))
it's this:
(-> x f (#(g h %)))
Saving 2 characters, like so, is not worth a breaking change IMO.
Hide
Alex Miller added a comment -

Retracted by submitter

Show
Alex Miller added a comment - Retracted by submitter
Alex Miller made changes -
Resolution Declined [ 2 ]
Status Open [ 1 ] Closed [ 6 ]

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved: