clojure-dev

2025-09-24T19:58:37.612949Z

are unmentioned exceptions part of the public api/contract of clojure core functions? i see that (vector-of :int ...) tests against ClassCastException when passed an incorrect type.

ghadi 2025-09-24T20:01:20.067299Z

no

ghadi 2025-09-24T20:02:36.770939Z

generally, we make contracts about what is accepted, and no promises around what happens when you do things outside of the positive contract

👍 2
ghadi 2025-09-24T20:02:58.257819Z

also, in general, we never promise concrete exception types

ghadi 2025-09-24T20:03:47.445419Z

e.g. we might sacrifice a kitten if you give a character to a (vector-of :int)

🙀 5
2025-09-24T20:04:13.831699Z

I know that the deref error message patch (https://clojure.atlassian.net/browse/CLJ-1162) was backed out of 1.12 when it was found datomic relied on the existing exception, but i'm wondering if that's a one-off or something y'all look for in general

ghadi 2025-09-24T20:04:46.481389Z

it wasn't because it relied on the existing exception

ghadi 2025-09-24T20:05:15.300449Z

but because Datomic relied on a particular call chain terminating

2025-09-24T20:05:27.315029Z

huh okay

ghadi 2025-09-24T20:06:45.753169Z

(there was a circularity between .get and .deref)

👍 1
ghadi 2025-09-24T20:07:17.908869Z

I work on Datomic, reviewed this IIRC

👀 2
2025-09-24T20:07:26.674319Z

Well, I'm working on https://clojure.atlassian.net/browse/CLJ-2822 for fun, and I think that changing intCast(Object n) to throw an UnsupportedOperation or IllegalArgument with a better error message would be helpful across the board, but the vector-of tests have given me pause

2025-09-24T20:09:31.810019Z

Most casts in Clojure aren't user-facing, they're implementation details, so imo saying something like "UnsupportedOperation: Expected int, received PersistentVector" is more readable and actually communicates the mismatch between user code and internals than "ClassCastException: Tried to cast PersistentVector to int"

ghadi 2025-09-24T20:13:41.767139Z

UnsupportedOperation: Expected int, received PersistentVector ClassCastException: Tried to cast PersistentVector to int those don't look substantially different to me.

ghadi 2025-09-24T20:14:12.583819Z

I realize the CCEs add noisy module info

2025-09-24T20:14:17.221679Z

lol the message can be massaged, i mostly mean that we write something intentional instead of leaving it up to the mechanical CCE

ghadi 2025-09-24T20:14:35.825109Z

you'll have to clear a very high bar for this

ghadi 2025-09-24T20:14:58.506049Z

the polymorphic cascade fns in RT are very perf sensitive

2025-09-24T20:15:57.493589Z

this is what i'm thinking, with whatever message makes sense

@@ -1238,7 +1252,9 @@ static public int intCast(Object x){
                long n = longCast(x);
                return intCast(n);
                }
-       return ((Character) x).charValue();
+       if(x instanceof Character)
+               return ((Character) x).charValue();
+       throw new IllegalArgumentException("Expected int, receieved " + x.getClass().getName());
 }

ghadi 2025-09-24T20:16:28.168879Z

non-starter for performance. There was another ticket that did this and it was walked back

2025-09-24T20:16:59.681569Z

oh?

ghadi 2025-09-24T20:17:18.688369Z

string concat blows up bytecode size and hurts inlining

2025-09-24T20:17:47.778089Z

enough things are cast from Character to ints that we have to worry about this?

ghadi 2025-09-24T20:17:49.956879Z

this is in somewhere even more critical than the nth impl in RT.java

ghadi 2025-09-24T20:19:50.185119Z

intCast is used literally everywhere when interoping with Java

ghadi 2025-09-24T20:20:16.621289Z

trying to save you wasted effort

2025-09-24T20:20:32.053589Z

this is all great info, i'm very grateful that you're explaining it

2025-09-24T20:20:57.297959Z

i have now been the primary clojure advocate at multiple jobs so i get to both show off clojure and also apologize for clojure a lot

2025-09-24T20:21:33.714539Z

even tho clojure is closer to "source available" than "open source", i remain dedicated to the goal of "making clojure easier to use and debug"

2025-09-24T20:22:45.340889Z

that's why i'm here, working on this

2025-09-24T20:22:52.852349Z

so any knowledge will be helpful for that goal

ghadi 2025-09-24T20:23:33.127059Z

"passed the wrong things" is a dev time goof that is very low priority to address

ghadi 2025-09-24T20:23:58.633549Z

sussed out quickly with effective use of the REPL

2025-09-24T20:24:00.080569Z

i've run into this on multiple occasions in datadog logs

2025-09-24T20:25:03.915379Z

sadly, we don't all get the opportunity to work on thoughtful codebases. i mostly work on things written by a succession of folks new to clojure and have to make do with what's available

2025-09-24T20:26:27.412019Z

lol the current codebase i'm spending my time on can't be run at the repl because it's too complected with the various external services it relies on and i'm not allowed the time to fix it

2025-09-24T20:29:11.327749Z

making errors better is helpful for everyone and shortens both the "easily sussed out" problems at the repl and the "i've spent multiple hours tearing my hair out" problems discovered in o11y logs

ghadi 2025-09-24T20:43:48.615739Z

spec instrumentation is a column in a decision matrix

ghadi 2025-09-24T20:44:00.169359Z

(however that conflicts with :inline, which nth is)

2025-09-24T20:44:32.682619Z

yeah if we remove inline, we don't even need spec instrumentation because the stack trace will say "error in clojure.core/nth"

2025-09-24T20:45:12.825749Z

my goal isn't "check inputs of nth", it's "error in a helpful way"

Alex Miller (Clojure team) 2025-09-24T20:55:57.140439Z

just to +1 stuff Ghadi said above: • in general, undefined behavior is undefined 😜 • if exception isn't mentioned in the docstring, should not consider the throwing of or type of to be contract • clojure tests for errors should not be considered indication of contract behavior • error construction is preferred out of mainline flow of control to support hotspot inlining

👍 1
2025-09-24T20:56:41.798619Z

what do you mean by that last line?

Alex Miller (Clojure team) 2025-09-24T20:56:57.834099Z

not sure if my edit helped?

ghadi 2025-09-24T20:58:13.882489Z

Outline into a separate method: fail(Object arg1)

2025-09-24T20:58:16.816839Z

i'll wait for you to finish typing lol

Alex Miller (Clojure team) 2025-09-24T20:58:21.185749Z

constructing an error is presumably exceptional behavior, not mainline code and often involves a lot of string/collection work - we usually prefer to move that work into a second method "throwError" that can be called when an error occurs, so that the messy stuff is not in the main code

👍 1
2025-09-24T21:01:24.251169Z

that makes sense

2025-09-24T21:03:19.868119Z

I'll leave the changes to intCast out of my nth patch

2025-09-26T15:43:55.185759Z

well, if either of you feel like spending some time looking at my work, i'd love the feedback: https://clojure.atlassian.net/browse/CLJ-2822

Alex Miller (Clojure team) 2025-09-26T16:26:41.907159Z

it's in my 1.13 list so will get to it at some point

🎉 1