clojure-dev

dgr 2026-04-21T21:20:21.831789Z

I was looking at testing lazy seqs for #clojure-test-suite. I see that the doc string for range says

Returns a lazy seq of nums from start (inclusive) to end (exclusive), by step, where start defaults to 0, step to 1, and end to infinity. When step is equal to 0, returns an infinite sequence of start. When start is equal to end, returns empty list.
But range actually returns a clojure.lang.LongRange or clojure.lang.Range not a clojure.lang.LazySeq. This pops up in that (realized? (range 10)) throws since range doesn’t return a lazy seq. The doc string for range seems stale (I’m guessing it did return a lazy seq at some point, but probably got optimized). If that’s true, what’s the correct way to determine if a range is realized? And yes, I know that I can for a lazy seq using (realized? (lazy-seq (range 10)) and that works. But that’s not what I’m after.

Alex Miller (Clojure team) 2026-04-21T21:23:36.154449Z

those things are lazy seqs (but not LazySeqs)

😵‍💫 1
dgr 2026-04-21T21:24:15.377639Z

Exactly

Alex Miller (Clojure team) 2026-04-21T21:24:28.137769Z

so doc string is correct

👎 1
Alex Miller (Clojure team) 2026-04-21T21:24:42.104179Z

I debated the realized? thing a lot during impl of LongRange and Range

dgr 2026-04-21T21:26:34.108059Z

I would say that the doc string is misleading.

Alex Miller (Clojure team) 2026-04-21T21:29:22.857349Z

well, they are lazy, and they are seqs. what should it say that would be better?

Alex Miller (Clojure team) 2026-04-21T21:30:31.499769Z

imo, realized? itself is broken in that it should never throw (I have a jira for this). it's current behavior is rarely ever useful and often a footgun

👍 1
dgr 2026-04-21T21:32:06.187849Z

If it's a lazy seq then realized? should work on it, right?

Alex Miller (Clojure team) 2026-04-21T21:32:26.025589Z

see https://clojure.atlassian.net/browse/CLJ-1752 (realized?) and https://clojure.atlassian.net/browse/CLJ-1751 (IPending on Range)

dgr 2026-04-21T21:32:30.790479Z

How do I detect whether a LongRange is realized?

Alex Miller (Clojure team) 2026-04-21T21:33:01.208319Z

it is effectively always realized, it is just math

Alex Miller (Clojure team) 2026-04-21T21:33:21.177559Z

or never realized, depending on how think about it :)

😂 1
dgr 2026-04-21T21:33:24.764499Z

Right. It's iterable and the math just backs that

dgr 2026-04-21T21:34:37.107539Z

Okay, lemme do some Jira reading and get a more informed opinion.

Alex Miller (Clojure team) 2026-04-21T21:35:01.546039Z

I have spent a truly indefensible amount of time working on the (re-)implementation of range in my life

😄 1
dgr 2026-04-21T21:35:10.517409Z

LOL

ghadi 2026-04-21T21:35:11.036429Z

same…

dgr 2026-04-21T21:35:36.399219Z

Okay, sorry for grinding on a sore point

Alex Miller (Clojure team) 2026-04-21T21:36:47.887019Z

oh no worries :)

dgr 2026-04-21T22:08:27.780529Z

OK, having looked at this for a few minutes and collected my thoughts, here’s what I think. It seems like the solution in CLJ-1752 is correct. Thus, a LongRange is always realized. @alexmiller, you said before that there’s a question of what realized? means. What did it mean before Clojure 1.7 for LazySeqs? I assume it would only be a question as to whether the first element in the seq was realized. And for LongRange, it always has that next value ready (because math). I assume that realized? could never have meant “Is the whole sequence realized?” because that would be potentially expensive. So, I would vote for CLJ-1752 so at least realized? doesn’t throw when given a LongRange.

👍 1
Alex Miller (Clojure team) 2026-04-21T22:26:22.820699Z

Yes, I think it must be about the first element, but the cases where asking even that question is useful are so rare and/or delicate that I struggle to find any case where someone could or should use it

dgr 2026-04-21T22:27:00.503909Z

I agree with that. I’ve never used realized? except for futures/promises/etc.

Alex Miller (Clojure team) 2026-04-21T22:27:05.404019Z

If you care about the answer, you should probably not be using lazy seqs

dgr 2026-04-21T22:27:12.314989Z

Right

Alex Miller (Clojure team) 2026-04-21T22:27:25.781929Z

Makes sense for future/ promise

dgr 2026-04-21T22:27:32.491489Z

Totally

dgr 2026-04-21T22:28:27.556499Z

So, to be clear, all my answers here are coming from the point of view of what is self-consistent for Clojure given the history and desire to preserve behavior. I’m specifically NOT saying what I would want or use.

👍 1
2026-04-21T22:28:39.634049Z

but given that it's both documented behavior and partially implemented, seems worthwhile to make it consistent

👍 1
dgr 2026-04-21T22:28:48.510009Z

Right

dgr 2026-04-21T22:29:24.370939Z

I’m testing it in #clojure-test-suite, so I want to know what is canonical behavior so that other dialects can conform to that.

2026-04-21T22:29:25.348139Z

the number of times alex has had to explain the pitfalls of realized? is enough to make your head spin

dgr 2026-04-21T22:29:34.218099Z

LOL

seancorfield 2026-04-21T22:43:46.744379Z

Perhaps just worth pointing out that realized? throws on "most" argument types. Maybe realized?'s docstring should be updated to mention IPending? In Clojure 1.6, (realized? (range 10)) was false. In Clojure 1.7, it throws. (realized? []), for example, throws in Clojure 1.6 (and 1.7 onward), so it was already a pretty niche function 🙂

dgr 2026-04-21T22:51:20.569109Z

Yea, it seemed like Clojure “evolved” there and realized? was one of those functions that only came along for half the ride.

👍 1
Alex Miller (Clojure team) 2026-04-21T23:02:37.569759Z

Again, the right solution is imo to “fix” realized?

Alex Miller (Clojure team) 2026-04-21T23:03:10.285349Z

Not worry about range or IPending (which is an implementation detail)

👍 2
dgr 2026-04-21T23:11:58.521129Z

Yes agreed.

ghadi 2026-04-21T23:30:43.875539Z

Test suites should be careful not to pour concrete on observed behaviors

👍 1
➕ 1
👆 1
dgr 2026-04-22T01:29:06.893169Z

That's precisely why I started this conversation. I want to clarify what the contract is or should be so that I'm not flagging behavior that is incidental and historical

👍 2
👍🏼 1