Fork me on GitHub
#clojure
<
2023-04-13
>
imre10:04:34

Hey folks. I recently saw @alexmiller wrote that eduction is eager (most recently in https://ask.clojure.org/index.php/12847/use-transducers-clojure-core-lazy-sequence-transformations?show=12848#a12848, and on slack https://clojurians.slack.com/archives/C053AK3F9/p1567446198032000 and https://clojurians.slack.com/archives/C053AK3F9/p1567447768035100?thread_ts=1567446353.034500&amp;cid=C053AK3F9). I'm somewhat confused by this, in my experience it only does as much work as it is asked to do. I'd appreciate some insight, as it appears I'm misunderstanding something either with eduction, or with what the things Alex wrote mean. Not sure if Alex himself is around to chime in perhaps? Example in the thread.

imre10:04:43

(let [ed (->> (range 500000)
              #_(into [])
              (eduction (map #(doto % print)))
              (eduction (map #(doto % (->> (str "    ") println)))))]

  (println "taking 3")
  (->> (into [] (take 3) ed)
       (println "taken 3:"))

  (println "getting first")
  (->> (first ed)
       (println "got first:"))

  (println "reducing first")
  (->> (reduce (comp reduced {}) nil ed)
       (println "reduced first:")))
taking 3
0    0
1    1
2    2
taken 3: [0 1 2]
getting first
0    0
1    1
2    2
3    3
4    4
5    5
6    6
7    7
8    8
9    9
10    10
11    11
12    12
13    13
14    14
15    15
16    16
17    17
18    18
19    19
20    20
21    21
22    22
23    23
24    24
25    25
26    26
27    27
28    28
29    29
30    30
31    31
32    32
got first: 0
reducing first
0    0
reduced first: 0
=> nil
While there is difference in how much is calculated, it appears that difference is due to how the eduction is consumed. Results appear to be the same regardless of whether the starting input is a lazy sequence or a vector (uncomment the into)

imre11:04:37

This is on clojure 1.11.1

imre11:04:29

and 1.12.0-alpha1

imre13:04:05

It occurred to me whether perhaps eduction eagerly consumes its input collection as opposed to eagerly running the calculations within so I did another test where I added prints when the input is realized:

imre13:04:20

(let [new-input (fn []
                  (->> (range 100)
                       (map #(doto % (->> (println "     init:"))))))
      xf1       (map #(doto % (->> (print "  processing:"))))
      xf2       (map #(doto % (->> (println "    "))))
      ed        (->> (new-input)
                     (eduction xf1)
                     (eduction xf2))]

  (println "=================================================\nTaking 3")
  (->> (into [] (take 3) ed)
       (println "Taken 3:"))

  (println "=================================================\nTaking 3 again")
  (->> (into [] (take 3) ed)
       (println "Taken 3 again:"))

  (println "\n=================================================\nGetting first")
  (->> (first ed)
       (println "Got first:"))

  (println "\n=================================================\nGetting first again")
  (->> (first ed)
       (println "Got first again:"))

  (println "\n=================================================\nReducing first")
  (->> (reduce (comp reduced {}) nil ed)
       (println "Reduced first:"))

  (println "\n=================================================\nTransducing first without eduction from new input")
  (->> (new-input)
       (transduce (comp xf1 xf2) (completing (comp reduced {})) nil)
       (println "Transduced:")))
=================================================
Taking 3
     init: 0
     init: 1
     init: 2
     init: 3
     init: 4
     init: 5
     init: 6
     init: 7
     init: 8
     init: 9
     init: 10
     init: 11
     init: 12
     init: 13
     init: 14
     init: 15
     init: 16
     init: 17
     init: 18
     init: 19
     init: 20
     init: 21
     init: 22
     init: 23
     init: 24
     init: 25
     init: 26
     init: 27
     init: 28
     init: 29
     init: 30
     init: 31
  processing: 0     0
  processing: 1     1
  processing: 2     2
Taken 3: [0 1 2]
=================================================
Taking 3 again
  processing: 0     0
  processing: 1     1
  processing: 2     2
Taken 3 again: [0 1 2]

=================================================
Getting first
  processing: 0     0
  processing: 1     1
  processing: 2     2
  processing: 3     3
  processing: 4     4
  processing: 5     5
  processing: 6     6
  processing: 7     7
  processing: 8     8
  processing: 9     9
  processing: 10     10
  processing: 11     11
  processing: 12     12
  processing: 13     13
  processing: 14     14
  processing: 15     15
  processing: 16     16
  processing: 17     17
  processing: 18     18
  processing: 19     19
  processing: 20     20
  processing: 21     21
  processing: 22     22
  processing: 23     23
  processing: 24     24
  processing: 25     25
  processing: 26     26
  processing: 27     27
  processing: 28     28
  processing: 29     29
  processing: 30     30
  processing: 31     31
     init: 32
     init: 33
     init: 34
     init: 35
     init: 36
     init: 37
     init: 38
     init: 39
     init: 40
     init: 41
     init: 42
     init: 43
     init: 44
     init: 45
     init: 46
     init: 47
     init: 48
     init: 49
     init: 50
     init: 51
     init: 52
     init: 53
     init: 54
     init: 55
     init: 56
     init: 57
     init: 58
     init: 59
     init: 60
     init: 61
     init: 62
     init: 63
  processing: 32     32
Got first: 0

=================================================
Getting first again
  processing: 0     0
  processing: 1     1
  processing: 2     2
  processing: 3     3
  processing: 4     4
  processing: 5     5
  processing: 6     6
  processing: 7     7
  processing: 8     8
  processing: 9     9
  processing: 10     10
  processing: 11     11
  processing: 12     12
  processing: 13     13
  processing: 14     14
  processing: 15     15
  processing: 16     16
  processing: 17     17
  processing: 18     18
  processing: 19     19
  processing: 20     20
  processing: 21     21
  processing: 22     22
  processing: 23     23
  processing: 24     24
  processing: 25     25
  processing: 26     26
  processing: 27     27
  processing: 28     28
  processing: 29     29
  processing: 30     30
  processing: 31     31
  processing: 32     32
Got first again: 0

=================================================
Reducing first
  processing: 0     0
Reduced first: 0

=================================================
Transducing first without eduction from new input
     init: 0
     init: 1
     init: 2
     init: 3
     init: 4
     init: 5
     init: 6
     init: 7
     init: 8
     init: 9
     init: 10
     init: 11
     init: 12
     init: 13
     init: 14
     init: 15
     init: 16
     init: 17
     init: 18
     init: 19
     init: 20
     init: 21
     init: 22
     init: 23
     init: 24
     init: 25
     init: 26
     init: 27
     init: 28
     init: 29
     init: 30
     init: 31
  processing: 0     0
Transduced: 0
=> nil

imre13:04:17

But it appears that isn't the case either (this is on 1.11.1, chunking doesn't seem to be working as I had expected in 1.12)

imre13:04:44

On the first take 3 the first 32 elements of the input are realized, then the first 3 are processed by the eduction(s). On the second take 3 enough elements of the input are realized already so no further init happens, only the re-processing of the first 3 as per the eduction docs. first wants to look at the first 33 elements for some reason so on the first invocation the second 32 element chunk is realized, then the first 33 elements are processed. On the second invocation, all the elements needed are already realized so eduction reprocesses the first 33. Reducing first only needs a single element and that's what eduction reprocesses so. For a comparison I reimplemented the reducing first with transduce to see how it works without the eduction and it doesn't seem any different.

Alex Miller (Clojure team)13:04:19

sorry, I was really referring to reduce in that sentence

imre13:04:01

Thanks for the response Alex 🙂 Does it also apply to this comment on slack? > eduction is pretty specialized in that it is a delayed eager evaluation https://clojurians.slack.com/archives/C053AK3F9/p1567446198032000

Alex Miller (Clojure team)14:04:26

it is often used in eager scenarios

imre14:04:45

but it isn't inherently eager then?

Alex Miller (Clojure team)14:04:13

no, it's iterator based, but often used with reduce, which is eager

imre14:04:05

Okay, thanks so much for taking the time to reply!

Alex Miller (Clojure team)14:04:54

sorry, have been too busy to reply to the other ask question yet

imre14:04:54

Not at all, I honestly really appreciate your approachability and how you interact with the community! I re-raised the question here mainly because my understanding of what you had stated was at odds with how I had understood eduction. I had refactored code in multiple places to use it once I got to know eduction and part of the reason for those refactorings was the capability of eduction to only calculate as many elements as absolutely necessary. I feared that if I misunderstood eduction these might cause problems and wanted to ask for community wisdom to help decide whether I need to revisit those places in code. Thank you again for your time!

Brian Beckman12:04:17

SOLVED Hello -- I stumbled on something that works, but I’m curious about how it works. I needed a function to be named Integer or something close to it, but that collides with the built-in java.lang.Integer. I thought about Integer- or Integer_ to sidestep the collision (I didn’t want -Integer as that connotes “private”). Then I recalled auto-gensyms in macros, and wrote something like

(defn Integer# [x] {:type 'Integer, :bytes 4, :value x})
(Integer# 42)
and it works fine! I’m not sure whether Integer# is actually an auto-gensymmed name, however, because my usage is not in a macro. I’m not sure how to find out, actually, because I’m not sure how to dig inside the symbol Integer# to find out what Clojure thinks it’s doing with it. I’m not losing sleep over this, because it works, but I am curious and would be grateful for some tricks to help me dig into symbols and figure out what’s inside, or for some simpleminded explanation of my stumble-upon :)

🏁 2
p-himik12:04:36

x# is just a symbol. It will turn into some other symbol only inside the syntax quote.

user=> `x#
x__2__auto__

👍 2
delaguardo12:04:49

it is not auto-gensymmed. defn itself is a macro so you can expand it to the code:

(def Integer# (fn* ([x] {:type 'Integer, :bytes 4, :value x})))
and because def is a special form Integer# stays as is. However, this symbol might cause problems if will use it inside of syntax quote:
(defmacro foo []
  `(do
     (prn "!!!")
     (prn (Integer# 1))))

(foo) ;; expands to (do (prn "!!!") (prn (Integer__11986__auto__ 1)))
;; => Syntax error compiling at (src/dev/dotfox/matchete.cljc:295:1).
;;    Unable to resolve symbol: Integer__11986__auto__ in this context

👍 2
p-himik12:04:45

Integer# will stay as is even when used as (defn Integer# ...).

👍 2
Brian Beckman12:04:05

appreciate it :) ty

borkdude12:04:57

@U03CZA5A539 You can use (ns-unmap *ns* 'Integer) and then simply call your function Integer. But sometimes people like to call these constructor functions ->Something, so maybe ->Integer isn't too bad either

👍 2
Brian Beckman14:04:45

@U04V15CAJ I like ->Something! I do need java.lang.Integer in the same namespace as my Integer constructor function, so unmapping java.lang.Integer wouldn't work off-the-cuff. Maybe I could rename java.lang.Integer instead? If so, how would I do that?

p-himik14:04:18

You could always refer to it by its full name. But please don't shadow it. It's much better to use ->Integer or any other variation if possible.

👍 2
☝️ 4
delaguardo14:04:35

fully qualified java.lang.Integer will be there even after unmapping

👍 2
Brian Beckman14:04:08

great info from both of you! this is going to solve my problem

grzm15:04:18

’lo all. I’ve run into a change in behavior between jdk 17 and jdk 19 with respect to coercion and java interop (I think). I’ve narrowed it down to whether or not a bind a value to a var: e.g., (all on Clojure 1.11.1)

(Thread/sleep (int 100)) ;; => works in jdk17 and jdk19
(def int-millis (int 100))
(Thread/sleep int-millis) ;; => works in jdk17, blows up in jdk19 with IllegalArgumentException: No matching method sleep found taking 1 args

grzm15:04:06

% cat thread-sleep.clj           
(println (format "clojure-version %s" (clojure-version)))
(println (format "java.version %s" (System/getProperty "java.version")))
(println "(Thread/sleep (int 100))")
(Thread/sleep (int 100))
(def int-millis (int 100))
(println "(Thread/sleep int-millis)")
(Thread/sleep int-millis)
(println "done")
jdk17:
% clj -Srepro -M thread-sleep.clj
clojure-version 1.11.1
java.version 17.0.6
(Thread/sleep (int 100))
(Thread/sleep int-millis)
done
jdk19:
% clj -Srepro -M thread-sleep.clj
clojure-version 1.11.1
java.version 19.0.2
(Thread/sleep (int 100))
(Thread/sleep int-millis)
Execution error (IllegalArgumentException) at user/eval11 (thread-sleep.clj:7).
No matching method sleep found taking 1 args

grzm15:04:23

Any ideas what I’m missing?

hiredman15:04:04

They added a new overload to sleep that takes a Duration and that is screwing with clojures ability to determine what method you are trying to call

grzm15:04:41

Gotcha. Thanks!

hiredman15:04:31

If you turn on reflection warnings you'll see it complain about not being able to figure it out

hiredman16:04:04

And if you pass a long instead of an int it will figure it out

grzm16:04:06

Good call.

grzm16:04:44

Yup, we found the long solution, but I hadn’t figured out the why. Thanks!

seancorfield16:04:10

I think I've opened issues against several repos about reflection warnings from that change (we keep a pretty close eye on reflection warnings at work).

borkdude16:04:19

I've ran into this specific case in a number of projects already

grzm16:04:11

@U04V15CAJ Is there anything in clj-kondo that can check whether I’ve remembered to enable reflection warnings for namespaces where I’ve done java interop?

grzm16:04:45

Perfect!

grzm16:04:54

Thanks, all!

borkdude16:04:51

@alexmiller Maybe Clojure should have a sleep function to avoid this interop/reflection issue over several version of the JVM :)

grzm16:04:16

What’s going on with the difference between (Thread/sleep (int 100)) and (Thread/sleep int-millis) ? What’s the difference in how the types are resolved?

grzm16:04:37

Maybe answering my own question: the var doesn’t have a type hint, and int likely does

borkdude16:04:54

@U0EHU1800 The type of a var is never inferred

grzm16:04:12

Yup. Makes sense.

grzm16:04:22

another shout-out to @U04V15CAJ and clj-kondo. It’s really been life-changing for my development and particularly maintenance of clojure projects. Thank you!

2
lread20:04:50

@U0EHU1800 I always use clj-kondo, but when doing interop I often also add https://github.com/jonase/eastwood to my lint pipeline to help find this kind of issue.

grzm20:04:15

It's so useful to see how others approach these types of things.

hiredman20:04:38

we use https://github.com/athos/clj-check at work, which scans for clj files and then loads each one it finds with *warn-on-reflection* set

Brian Beckman17:04:47

SOLVED Hello — this question concerns writing macros to smooth out some multi-specs. Consider the following multi-spec declaration:

(do (defmulti ttype-head ::ttype-head)
    (s/def ::asr-ttype-head
      (s/multi-spec ttype-head ::ttype-head)))
and the following two instances:
(defmethod ttype-head ::Integer [_]
  (s/keys :req [::ttype-head ::bytes-kind ::dimensions]))
(defmethod ttype-head ::Real [_]
  (s/keys :req [::ttype-head ::bytes-kind ::dimensions]))
These seem to work fine, but I want to mitigate the repetition. I write:
(defmacro def-ttype-head [it]
  `(defmethod ttype-head ~it [_]
     (s/keys :req [::ttype-head ::bytes-kind ::dimensions])))
CIDER macro-expand-1 shows everything looking good:
(def-ttype-head ::Integer) 
;; =>
(defmethod
  ttype-head
  :masr.specs/Integer
  [_]
  (s/keys
    :req
    [:masr.specs/ttype-head
     :masr.specs/bytes-kind
     :masr.specs/dimensions]))
But, when I do the full macroexpand, the compiler barfs off with something I don’t understand:
2. Unhandled clojure.lang.ExceptionInfo
   Call to clojure.core/fn did not conform to spec.
   #:clojure.spec.alpha{:problems ({:path [:fn-tail :arity-1 :params], :reason "Extra input",  
...
1. Caused by clojure.lang.Compiler$CompilerException
   Error compiling /private/var/folders/cp/df32wpxn1ps8dmn6w_tydwdr0000gn/T/form-init13650237565921167220.clj at (1:8259)
   #:clojure.error{:phase :macro-syntax-check, :line 1, :column 8259, :source "/private/var/folders/cp/df32wpxn1ps8dmn6w_tydwdr0000gn/T/form-init13650237565921167220.clj", :symbol clojure.core/fn}
             Compiler.java: 6989  clojure.lang.Compiler/checkSpecs
             Compiler.java: 7005  clojure.lang.Compiler/macroexpand1
...
I haven’t spotted the error. I’d be grateful for an explanation and - or a fix!

🏁 2
phronmophobic17:04:29

Not totally sure it's the only problem, but the _ symbol in the argument list should probably be a gensym _#

👍 2
phronmophobic17:04:53

(defmacro def-ttype-head [it]
  `(defmethod ttype-head ~it [_#]
     (s/keys :req [::ttype-head ::bytes-kind ::dimensions])))

phronmophobic17:04:16

I think you can also do:

(s/def ::ttype-head (s/keys :req [::ttype-head ::bytes-kind ::dimensions]))
(defmethod ttype-head ::Integer [_]
  ::ttype-head)

👍 2
Alex Miller (Clojure team)17:04:19

note that CIDER does not use the error triage and printing system that we spent so much time making in Clojure 1.10, but if you use a clojure.main repl it would have said:

user=> (def-ttype-head ::Integer)
Syntax error macroexpanding clojure.core/fn at (REPL:1:1).
(user/_) - failed: Extra input at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
user/_ - failed: vector? at: [:fn-tail :arity-n :params] spec: :clojure.core.specs.alpha/param-list
which seems a lot more helpful pointing you in the right direction

👍 2
Brian Beckman18:04:31

@U7RJTCH6J would the spec ::ttype-head collide with the multi-spec (s/multi-spec ttype-head ::ttype-head)?

phronmophobic18:04:25

Yea, it would. You would have to use a different name

Brian Beckman18:04:07

roger. Thanks for the sharp reading :)

Brian Beckman18:04:59

ASIDE: this discussion helped me a lot. I just wrote a significant new macro and debugged the names given your teachings 🙂

(defmacro def-ttype-sugar [the-head]
  `(defn ~the-head
     [& {kind# :kind, dimensions# :dimensions,
         :or {kind# 4, dimensions# []}}] ;; defaults
     (let [cnf# (s/conform
                 ::asr-ttype-head
                 {::ttype-head
                  (keyword (str ~the-head))
                  ::bytes-kind kind#,
                  ::dimensions dimensions#})]
       (if (s/invalid? cnf#)
         (keyword "masr.specs" (str "invalid-" ~the-head))
         cnf#))))

Brian Beckman18:04:26

that has nested multi-specs :)

Alex Miller (Clojure team)17:04:22

what's the stuff you hid in the first ... ?

Brian Beckman17:04:04

@alexmiller here are all …

Show: Project-Only All 
  Hide: Clojure Java REPL Tooling Duplicates  (26 frames hidden)

2. Unhandled clojure.lang.Compiler$CompilerException
   Error compiling /Users/brian/Documents/GitHub/masr/src/masr/specs.clj at (827:1)
   #:clojure.error{:phase :macro-syntax-check,
                   :line 827,
                   :column 1,
                   :source
                   "/Users/brian/Documents/GitHub/masr/src/masr/specs.clj",
                   :symbol clojure.core/fn}
             Compiler.java: 6989  clojure.lang.Compiler/checkSpecs
             Compiler.java: 7005  clojure.lang.Compiler/macroexpand1
             Compiler.java: 7110  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6806  clojure.lang.Compiler/analyze
             Compiler.java: 6762  clojure.lang.Compiler/analyze
             Compiler.java: 1020  clojure.lang.Compiler$HostExpr$Parser/parse
             Compiler.java: 7124  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6806  clojure.lang.Compiler/analyze
             Compiler.java: 6762  clojure.lang.Compiler/analyze
             Compiler.java: 6137  clojure.lang.Compiler$BodyExpr$Parser/parse
             Compiler.java: 5479  clojure.lang.Compiler$FnMethod/parse
             Compiler.java: 4041  clojure.lang.Compiler$FnExpr/parse
             Compiler.java: 7122  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6806  clojure.lang.Compiler/analyze
             Compiler.java: 7191  clojure.lang.Compiler/eval
             Compiler.java: 7149  clojure.lang.Compiler/eval
                  core.clj: 3215  clojure.core/eval
                  core.clj: 3211  clojure.core/eval
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  667  clojure.core/apply
                  core.clj: 1990  clojure.core/with-bindings*
                  core.clj: 1990  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  152  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  218  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  217  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java: 1589  java.lang.Thread/run

1. Caused by clojure.lang.ExceptionInfo
   Call to clojure.core/fn did not conform to spec.
   #:clojure.spec.alpha{:problems
                        ({:path [:fn-tail :arity-1 :params],
                          :reason "Extra input",
                          :pred
                          (clojure.spec.alpha/cat
                           :params
                           (clojure.spec.alpha/*
                            :clojure.core.specs.alpha/binding-form)
                           :var-params
                           (clojure.spec.alpha/?
                            (clojure.spec.alpha/cat
                             :ampersand
                             #{'&}
                             :var-form
                             :clojure.core.specs.alpha/binding-form))),
                          :val (masr.specs/_),
                          :via
                          [:clojure.core.specs.alpha/params+body
                           :clojure.core.specs.alpha/param-list
                           :clojure.core.specs.alpha/param-list],
                          :in [0 0]}
                         {:path [:fn-tail :arity-n :params],
                          :pred clojure.core/vector?,
                          :val masr.specs/_,
                          :via
                          [:clojure.core.specs.alpha/params+body
                           :clojure.core.specs.alpha/params+body
                           :clojure.core.specs.alpha/params+body
                           :clojure.core.specs.alpha/param-list
                           :clojure.core.specs.alpha/param-list],
                          :in [0 0]}),
                        :spec
                        #object[clojure.spec.alpha$regex_spec_impl$reify__2503 0x404deb91 "clojure.spec.alpha$regex_spec_impl$reify__2503@404deb91"],
                        :value
                        ([masr.specs/_]
                         (clojure.spec.alpha/keys
                          :req
                          [:masr.specs/ttype-head
                           :masr.specs/bytes-kind
                           :masr.specs/dimensions])),
                        :args
                        ([masr.specs/_]
                         (clojure.spec.alpha/keys
                          :req
                          [:masr.specs/ttype-head
                           :masr.specs/bytes-kind
                           :masr.specs/dimensions]))}
                 alpha.clj:  712  clojure.spec.alpha/macroexpand-check
                 alpha.clj:  704  clojure.spec.alpha/macroexpand-check
                  AFn.java:  156  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  Var.java:  705  clojure.lang.Var/applyTo
             Compiler.java: 6987  clojure.lang.Compiler/checkSpecs
             Compiler.java: 7005  clojure.lang.Compiler/macroexpand1
             Compiler.java: 7110  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6806  clojure.lang.Compiler/analyze
             Compiler.java: 6762  clojure.lang.Compiler/analyze
             Compiler.java: 1020  clojure.lang.Compiler$HostExpr$Parser/parse
             Compiler.java: 7124  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6806  clojure.lang.Compiler/analyze
             Compiler.java: 6762  clojure.lang.Compiler/analyze
             Compiler.java: 6137  clojure.lang.Compiler$BodyExpr$Parser/parse
             Compiler.java: 5479  clojure.lang.Compiler$FnMethod/parse
             Compiler.java: 4041  clojure.lang.Compiler$FnExpr/parse
             Compiler.java: 7122  clojure.lang.Compiler/analyzeSeq
             Compiler.java: 6806  clojure.lang.Compiler/analyze
             Compiler.java: 7191  clojure.lang.Compiler/eval
             Compiler.java: 7149  clojure.lang.Compiler/eval
                  core.clj: 3215  clojure.core/eval
                  core.clj: 3211  clojure.core/eval
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  667  clojure.core/apply
                  core.clj: 1990  clojure.core/with-bindings*
                  core.clj: 1990  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  152  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  218  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  217  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java: 1589  java.lang.Thread/run

Brian Beckman18:04:54

I see the _ in the chaff, there. I might eventually have found it :thinking_face:

Alex Miller (Clojure team)18:04:48

clojure.main doing a lot better job surfacing that for you, if only CIDER used it

👍 2