Clojure

Hints don't work with #() form of function

Details

  • Type: Enhancement Enhancement
  • Status: Open Open
  • Priority: Major Major
  • Resolution: Unresolved
  • Affects Version/s: None
  • Fix Version/s: Release 1.7
  • Component/s: None
  • Labels:
  • Patch:
    Code
  • Approval:
    Vetted

Description

;; WORKS
(deftest test-add-job
  (let [pool (java.util.concurrent.Executors/newFixedThreadPool 10)
        counter 50000
        f (fn [])]
    (dotimes [i counter]
      (.submit pool ^Runnable  f ))
    (Thread/sleep 1000)
    (is (= counter (count (all-jobs))))))

;; FAILS
(deftest test-add-job
  (let [pool (java.util.concurrent.Executors/newFixedThreadPool 10)
        counter 50000]
    (dotimes [i counter]
      (.submit pool ^Runnable  #()))
    (Thread/sleep 1000)
    (is (= counter (count (all-jobs))))))

Caused by: java.lang.IllegalArgumentException: More than one matching method found: submit
	at clojure.lang.Compiler.getMatchingParams(Compiler.java:2380)
	at clojure.lang.Compiler$InstanceMethodExpr.<init>(Compiler.java:1412)
	at clojure.lang.Compiler$HostExpr$Parser.parse(Compiler.java:952)
	at clojure.lang.Compiler.analyzeSeq(Compiler.java:6560)
	... 87 more
  1. clj-1378.diff
    12/Mar/14 4:16 AM
    0.8 kB
    Jozef Wagner
  2. clj-1378-v2.diff
    12/Mar/14 2:51 PM
    2 kB
    Jozef Wagner

Activity

Alex Miller made changes -
Field Original Value New Value
Description ;; WORKS
(deftest test-add-job
  (let [pool (java.util.concurrent.Executors/newFixedThreadPool 10)
        counter 50000
        f (fn [])]
    (dotimes [i counter]
      (.submit pool ^Runnable f ))
    (Thread/sleep 1000)
    (is (= counter (count (all-jobs))))))

;; FAILS
(deftest test-add-job
  (let [pool (java.util.concurrent.Executors/newFixedThreadPool 10)
        counter 50000]
    (dotimes [i counter]
      (.submit pool ^Runnable #()))
    (Thread/sleep 1000)
    (is (= counter (count (all-jobs))))))

Caused by: java.lang.IllegalArgumentException: More than one matching method found: submit
at clojure.lang.Compiler.getMatchingParams(Compiler.java:2380)
at clojure.lang.Compiler$InstanceMethodExpr.<init>(Compiler.java:1412)
at clojure.lang.Compiler$HostExpr$Parser.parse(Compiler.java:952)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6560)
... 87 more
{code}
;; WORKS
(deftest test-add-job
  (let [pool (java.util.concurrent.Executors/newFixedThreadPool 10)
        counter 50000
        f (fn [])]
    (dotimes [i counter]
      (.submit pool ^Runnable f ))
    (Thread/sleep 1000)
    (is (= counter (count (all-jobs))))))

;; FAILS
(deftest test-add-job
  (let [pool (java.util.concurrent.Executors/newFixedThreadPool 10)
        counter 50000]
    (dotimes [i counter]
      (.submit pool ^Runnable #()))
    (Thread/sleep 1000)
    (is (= counter (count (all-jobs))))))

Caused by: java.lang.IllegalArgumentException: More than one matching method found: submit
at clojure.lang.Compiler.getMatchingParams(Compiler.java:2380)
at clojure.lang.Compiler$InstanceMethodExpr.<init>(Compiler.java:1412)
at clojure.lang.Compiler$HostExpr$Parser.parse(Compiler.java:952)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:6560)
... 87 more
{code}
Hide
Jozef Wagner added a comment -

Functions do have metadata, but Compiler does not look in them for type hints.

user=> (with-meta #() {:foo :bar})
#<clojure.lang.AFunction$1@779325ee>

When compiler is determining which native method to use, it matches method signature with classes of given args. There is a getJavaClass() method in Compiler.java which returns a class for given expression. Vars expressions and local bindings use :tag metadata to override this class, but most other expressions don't. Compiler parses #() into a FnExpr, which always return AFunction as its class.

Most of time this approach is OK, as AFunction implements Runnable and Callable so there is no need for type hint. However, in this particular case, there are overrides for both Runnable and Callable, and as AFunction can be either of them, the expression is ambiguous.

Show
Jozef Wagner added a comment - Functions do have metadata, but Compiler does not look in them for type hints.
user=> (with-meta #() {:foo :bar})
#<clojure.lang.AFunction$1@779325ee>
When compiler is determining which native method to use, it matches method signature with classes of given args. There is a getJavaClass() method in Compiler.java which returns a class for given expression. Vars expressions and local bindings use :tag metadata to override this class, but most other expressions don't. Compiler parses #() into a FnExpr, which always return AFunction as its class. Most of time this approach is OK, as AFunction implements Runnable and Callable so there is no need for type hint. However, in this particular case, there are overrides for both Runnable and Callable, and as AFunction can be either of them, the expression is ambiguous.
Jozef Wagner made changes -
Attachment clj-1378.diff [ 12869 ]
Hide
Jozef Wagner added a comment -

Patch added, following expression will now run without error

(.submit (java.util.concurrent.Executors/newCachedThreadPool) ^Runnable #())
Show
Jozef Wagner added a comment - Patch added, following expression will now run without error
(.submit (java.util.concurrent.Executors/newCachedThreadPool) ^Runnable #())
Alex Miller made changes -
Comment [ I suspect this is because functions don't have metadata. Metadata is attached to vars or locals (neither of which exist in the anonymous function case). So the issue is not really the anonymous function, but that there is no place to hang the meta. For example this example using #() is fine:

{code}
;; WORKS
(deftest test-add-job
(let [pool (java.util.concurrent.Executors/newFixedThreadPool 10)
counter 50000
f #()]
(dotimes [i counter]
(.submit pool ^Runnable f ))
(Thread/sleep 1000)
(is (= counter (count (all-jobs))))))
{code} ]
Alex Miller made changes -
Patch Code [ 10001 ]
Approval Triaged [ 10120 ]
Labels interop typehints
Issue Type Defect [ 1 ] Enhancement [ 4 ]
Hide
Alex Miller added a comment -

Could you add a test to the patch?

Show
Alex Miller added a comment - Could you add a test to the patch?
Jozef Wagner made changes -
Attachment clj-1378-v2.diff [ 12872 ]
Hide
Jozef Wagner added a comment -

Attached patch clj-1378-v2.diff which contains both fix and test.

Show
Jozef Wagner added a comment - Attached patch clj-1378-v2.diff which contains both fix and test.
Rich Hickey made changes -
Approval Triaged [ 10120 ] Vetted [ 10003 ]
Rich Hickey made changes -
Fix Version/s Release 1.7 [ 10250 ]

People

Vote (1)
Watch (1)

Dates

  • Created:
    Updated: