clojure-dev

Issues: https://clojure.atlassian.net/browse/CLJ | Guide: https://insideclojure.org/2015/05/01/contributing-clojure/
dominicm 2019-07-08T06:36:43.372Z

Given that (.getResource (var-get clojure.lang.Compiler/LOADER) "cognitect/test_runner.clj") works, why wouldn't (require 'cognitect.test-runner) given that the failure is FileNotFoundException Could not locate Clojure resource on classpath: cognitect/test_runner.clj clojure.lang.RT.loadResourceScript (RT.java:388). Disclaimer: I'm using a custom classloader.

dominicm 2019-07-08T06:58:02.372600Z

Ah, clojure uses getResourceAsStream instead of getResource, I think the class loader is broken for AsStream with files. Bummer.

slipset 2019-07-08T07:39:18.373Z

Would a ticket for improving this error message be welcome?

slipset 2019-07-08T07:39:24.373300Z

09:33 $ clj
Clojure 1.10.0
user=> (let [{:keys [foo]} 3] (println "foo"))
Syntax error (UnsupportedOperationException) compiling at (REPL:1:1).
Can't type hint a primitive local
user=>

mfikes 2019-07-08T16:54:33.374500Z

Seems like ^ is a bug (it shouldn't fail at all)

ghadi 2019-07-08T17:27:00.375400Z

what's slightly confusing is that there are no type hints in the user code, it's coming from destructuring macroexpansion

ghadi 2019-07-08T17:27:41.376200Z

I'd be curious to see if this repro was distilled from a different scenario in the wild @slipset

slipset 2019-07-08T17:34:56.380900Z

@ghadi No, I was rewriting some code at work and made the mistake of trying to destructure a number. So apart from naming the key foo and trying to print it, this was code I actually wrote.

slipset 2019-07-08T17:46:13.381100Z

It comes from here:

slipset 2019-07-08T17:46:46.381600Z

user=> *e
#error {
 :cause "Can't type hint a primitive local"
 :via
 [{:type clojure.lang.Compiler$CompilerException
   :message "Syntax error compiling at (1:1)."
   :data #:clojure.error{:phase :compile-syntax-check, :line 1, :column 1, :source "NO_SOURCE_PATH"}
   :at [clojure.lang.Compiler analyze "Compiler.java" 6808]}
  {:type java.lang.UnsupportedOperationException
   :message "Can't type hint a primitive local"
   :at [clojure.lang.Compiler$LocalBindingExpr <init> "Compiler.java" 6015]}]
 :trace
 [[clojure.lang.Compiler$LocalBindingExpr <init> "Compiler.java" 6015]
  [clojure.lang.Compiler analyzeSymbol "Compiler.java" 7297]
  [clojure.lang.Compiler analyze "Compiler.java" 6768]
  [clojure.lang.Compiler analyze "Compiler.java" 6745]
  [clojure.lang.Compiler$InvokeExpr parse "Compiler.java" 3888]
  [clojure.lang.Compiler analyzeSeq "Compiler.java" 7108]
  [clojure.lang.Compiler analyze "Compiler.java" 6789]
  [clojure.lang.Compiler analyze "Compiler.java" 6745]
  [clojure.lang.Compiler$HostExpr$Parser parse "Compiler.java" 1020]

slipset 2019-07-08T17:51:27.382100Z

So the macroexpanded thing looks like:

user=> (clojure.pprint/pprint (macroexpand '(let [{:keys [foo]} 3] (println foo))))
(let*
 [map__165
  3
  map__165
  (if
   (clojure.core/seq? map__165)
   (clojure.lang.PersistentHashMap/create (clojure.core/seq map__165))
   map__165)
  foo
  (clojure.core/get map__165 :foo)]
 (println foo))
nil
user=>

slipset 2019-07-08T17:52:18.383Z

Which at least seems to have a redundant call to seq in the true branch of the if test.

slipset 2019-07-08T17:52:32.383300Z

Compare this with planck:

slipset 2019-07-08T17:52:47.383500Z

cljs.user=> (macroexpand '(let [{:keys [foo]} 3] (println foo)))
(let* [map__24 3
       map__24 (if (cljs.core/implements? cljs.core/ISeq map__24) (cljs.core/apply cljs.core/hash-map map__24) map__24)
       foo (cljs.core/get map__24 :foo)]
  (println foo))
cljs.user=>

bronsa 2019-07-08T18:02:06.383800Z

repro

user=> (let [x 1] (when (seq? x) (.first ^clojure.lang.ISeq x)))
Syntax error (UnsupportedOperationException) compiling at (REPL:1:27).
Can't type hint a primitive local

bronsa 2019-07-08T18:05:52.384400Z

doesn't seem to me like that type hint is needed

slipset 2019-07-08T18:10:02.385800Z

Even if it is needed, wouldn’t it be safer to add it at the point where create is called?

bronsa 2019-07-08T18:10:07.385900Z

yeah it was needed when the macro expanded to (c.l.PHM/create gmapseq) instead of (c.l.PHM/create (seq gmapseq))

bronsa 2019-07-08T18:10:20.386200Z

it's useless right now

slipset 2019-07-08T18:12:07.387Z

Ah, so we need to somehow coerce/typehint the gmap local to get the correct version of c.l.PHM/create?

bronsa 2019-07-08T18:12:24.387300Z

no, the current implementation doesn't need it

bronsa 2019-07-08T18:12:52.388500Z

a previous implementation did, and so the type hinting was in place, but then a call to seq was introduced which rendered the type hint unnecessary but i guess it was just forgotten there

devn 2019-07-08T18:13:05.389100Z

JIRA seems really slow. Not sure if there's something wrong, but for instance I am stuck at a loading screen here: https://clojure.atlassian.net/projects/TCLI/issues

slipset 2019-07-08T18:13:26.389800Z

so could we just do (c.l.PHM/create gmap) (since we know from the if thest that gmap is a seq?

bronsa 2019-07-08T18:13:28.390Z

-                           gmapseq (with-meta gmap {:tag 'clojure.lang.ISeq})
                            defaults (:or b)]
                        (loop [ret (-> bvec (conj gmap) (conj v)
-                                      (conj gmap) (conj `(if (seq? ~gmap) (clojure.lang.PersistentHashMap/create (seq ~gmapseq)) ~gmap))
+                                      (conj gmap) (conj `(if (seq? ~gmap) (clojure.lang.PersistentHashMap/create (seq ~gmap)) ~gmap))

bronsa 2019-07-08T18:13:32.390200Z

we could do this now

ghadi 2019-07-08T18:13:34.390400Z

@devn I think there are some 'issues' with it... :simple_smile:

devn 2019-07-08T18:13:46.390900Z

yeah, i clicked a couple times and now it seems to load All Issues

ghadi 2019-07-08T18:13:48.391Z

also the SSO gets confused

bronsa 2019-07-08T18:14:10.391800Z

I for some reason can't login from firefox just on linux

➕ 2
ghadi 2019-07-08T18:19:01.394Z

@bronsa @slipset this seems really low on the priority list. the input code doesn't make sense, and removing that type hint (while it is unnecessary) will make it silently be accepted

seancorfield 2019-07-08T18:19:24.394800Z

The issues page never loads (just spins) if you're logged into JIRA with a different account (or perhaps just not logged into your Clojure account?).

ghadi 2019-07-08T18:19:55.395600Z

another possibility would be forbidding numbers on the right hand side of a map/vector destructuring

seancorfield 2019-07-08T18:20:09.396Z

You can do other stuff in JIRA with projects but the issues page won't load for un-auth'd accounts @devn

ghadi 2019-07-08T18:20:11.396100Z

with defmacro specs

bronsa 2019-07-08T18:20:37.396500Z

agreed

slipset 2019-07-08T18:21:04.396900Z

No worries, that’s why I asked before creating an issue.

slipset 2019-07-08T18:22:46.398100Z

I guess my main point was that while happy it didn’t fail silently, I was a bit surprised by the error message, and since those have been a priority lately, I figured it would be worth mentioning.

ghadi 2019-07-08T18:23:32.398600Z

it's a surprising error for sure, totally unrelated to the input code

ghadi 2019-07-08T18:23:46.398900Z

just don't want to jump to a solution

ghadi 2019-07-08T18:24:33.400100Z

https://github.com/clojure/core.specs.alpha/blob/master/src/main/clojure/clojure/core/specs/alpha.clj#L53 perhaps init-exprs shouldn't be any? -- maybe they can be sensitive to the left hand side

slipset 2019-07-08T18:24:46.400500Z

“Non-sensical destructuring fails with surprising error message”

slipset 2019-07-08T18:25:02.400800Z

Probably my best Jira summary ever.

ghadi 2019-07-08T18:25:16.401100Z

sounds good to me

mfikes 2019-07-08T18:37:56.402800Z

Is associative destructuring sugar on top of get? Or is that never specified anywhere? (The reason I ask is that, if so, it seems to be related to whether get on a number is meant to return nil.)

ghadi 2019-07-08T18:38:23.403100Z

yes and yes, (get 3 :foo) returns nil

ghadi 2019-07-08T18:39:21.404300Z

this is a number at macroexpansion time, and I don't think that can ever make sense

ghadi 2019-07-08T18:39:44.404900Z

(let [{:keys [a]} 42] ...)

slipset 2019-07-08T18:41:08.406300Z

if (get 3 :foo) makes sense then I guess (let [{:keys [foo]} 3]..) makes sense as well?

mfikes 2019-07-08T18:41:33.406800Z

Cool, just trying to keep it straight in my head (outside of this nonsensical example). Associative destructuring is sugar for get and sequential is sugar for nth with nil for unknown, in my simplified mental model.

ghadi 2019-07-08T18:42:04.407300Z

(get 3 :foo) does not make sense, even though it (correctly) doesn't blow up

ghadi 2019-07-08T18:42:31.408100Z

but most of the time you don't know it's a constant

mfikes 2019-07-08T18:42:33.408300Z

Right, and we will never really know whether (get 3 :foo) is meant to return nil or not, I bet.

ghadi 2019-07-08T18:42:50.408900Z

we do -- it is supposed to return nil

mfikes 2019-07-08T18:42:56.409300Z

Says who?

ghadi 2019-07-08T18:43:04.409500Z

says rich

ghadi 2019-07-08T18:43:20.410Z

there's an explicit bottom-out in RT.getFrom()

ghadi 2019-07-08T18:43:38.410500Z

get on something that doesn't know how to get returns nil

mfikes 2019-07-08T18:44:05.411300Z

Yeah, that's what I resorted to as the spec of the behavior as well. Using the impl as the spec.

ghadi 2019-07-08T18:44:41.411900Z

this one is by design

slipset 2019-07-08T18:45:08.412700Z

are you implying there are things in Clojure which are not by design?

mfikes 2019-07-08T18:45:10.412800Z

If we accept that, then Eric's last point makes sense.

ghadi 2019-07-08T18:45:49.413500Z

(get 3 :foo) returns the correct thing (nil) but makes no sense from a user perspective

ghadi 2019-07-08T18:46:04.413800Z

UB

ghadi 2019-07-08T18:46:37.414600Z

destructuring is slightly different in that you have the opportunity to check a spec at macroexpansion time

mfikes 2019-07-08T18:47:56.415100Z

Speculative decided to allow any for the map argument to get https://github.com/borkdude/speculative/blob/master/src/speculative/core.cljc#L203

slipset 2019-07-08T18:49:43.416300Z

I agree with @ghadi, since

user=> (def lol 3)
#'user/lol
user=> (let [{:keys [foo]} lol] (println foo))
works as expected

ghadi 2019-07-08T18:50:23.417Z

that works because it is not known that it is a compile time constant

ghadi 2019-07-08T18:50:34.417500Z

there's no inference across var deref

ghadi 2019-07-08T18:50:59.418100Z

((fn [x] (get x :foo)) 3) would be another example

slipset 2019-07-08T18:51:59.419100Z

mmm, I guess my point was that if the above example would fail with an error, I’d be a little unhappy, because it’s not always clear that a var contains a constant

slipset 2019-07-08T18:52:40.420100Z

where as (let [{:keys [foo]} 3] ...) is just plain, well, non-sensical.

slipset 2019-07-08T18:59:35.421400Z

BTW, since you linked to https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/RT.java#L786

slipset 2019-07-08T18:59:53.421900Z

(and I’m not suggesting a rewrite/refactor, just being curious)

slipset 2019-07-08T19:01:19.423400Z

are there reasons (like performance), for duplicating the code in get(Object coll, Object key, Object notFound) and get(Object coll, Object key)?

slipset 2019-07-08T19:01:51.424Z

I’d imagine that the latter could be implemented in terms of the former?

slipset 2019-07-08T19:02:26.424500Z

(too long since I programmed Java to remember if there are problems related to overloading static fns)

ghadi 2019-07-08T19:02:58.424900Z

performance for that one

ghadi 2019-07-08T19:03:22.425400Z

get(m k not-found) on a java.util.Map has to check contains first, then call get

ghadi 2019-07-08T19:03:54.426Z

though on an ILookup is simply m.valAt(k, notfound)

ghadi 2019-07-08T19:04:17.426500Z

same reason get() and getFrom() is split, too: performance. Put the fastpath into a small, fast method

ghadi 2019-07-08T19:04:31.426800Z

nth / nthFrom, same deal

slipset 2019-07-08T19:04:42.427Z

So that it can be inlined?

ghadi 2019-07-08T19:04:56.427300Z

yeah

slipset 2019-07-08T19:05:17.427700Z

Cool, thanks 🙂