Fork me on GitHub
#clojurescript
<
2020-07-24
>
pmooser13:07:40

Ok guys. I feel like I am losing my mind.

pmooser13:07:25

Does it seem sensible to anyone that this range (range 0 (+ 1 (/ 9)) (/ 9)) has 11 elements in cljs (I'm not sure why), but if you convert it to a vector, it has 10?

pmooser13:07:03

It's really really weird to do things like have a range with 11 elements, but if you call butlast on it, it suddenly has 9.

pmooser13:07:41

I mean I assume this all has to do with numeric precision issues somehow but I have to imagine this is a bug.

thheller13:07:12

I don't have a clue but my guess would be thats because clojurescript doesn't have true ratios so 1/9 is just a float as opposed to clojure

pmooser13:07:46

Yes, certainly. But I still think the behavior is wrong with regard to length and conversions.

pmooser13:07:56

It's weird to print it out and get a value that doesn't exist at all if you convert it to a vec.

pmooser13:07:24

It means I won't be using range for this case, but it's still a head scratcher.

thheller13:07:44

probably just a bug in the range impl. count is doing math instead of doing actual seq stuff.

dnolen15:07:04

the issue is just that range only should take ints but your code will produce floating point (in ClojureScript)

dnolen15:07:15

thus introduce a bunch of problems

dnolen15:07:25

@pmooser I'm also not totally sure what you are trying to accomplish since in Clojure that would produce rationals

dnolen15:07:38

which don't exist in ClojureScript

dnolen15:07:14

so trying to determine if you're just doing something fundamentally that's not going to work that involves rationals

dnolen15:07:23

or actually need to pass floating point to range

dnolen15:07:10

and expecting some kind of behavior - but I don't see what since it couldn't possible align with Clojure

pmooser15:07:12

@dnolen It's just math, that's what I'm trying to accomplish. If range should take only ints, I would love it if that fact was mentioned in the doc string.

pmooser15:07:35

I'll make sure to avoid using range for anything non-integral in the future, but I checked the doc string and found no mention of that limitation.

pmooser15:07:04

When I'm doing something with math or lots of floating point, I don't tend to share the exact same code between clojure and cljs due to their fundamental differences in underlying numeric types anyway. What I pasted here was a simplified expression that had the same behavior I was curious about in cljs.

pmooser16:07:20

I understand that you can't take the time to explain it, but I'm sort of still interested in why this occurs. It's not like an issue with float precision ... the final value is obviously way outside the range.

dnolen16:07:04

@pmooser well only ints is wrong

dnolen16:07:09

0.5 etc will work

dnolen16:07:14

but not rationals

dnolen16:07:36

we could add docstrings all over the place about this but it wouldn't be that productive in my opinion

dnolen16:07:04

if not already covered in the more general difference page - it should be

dnolen16:07:40

as far as I can tell there's no magical way to make what you want work

dnolen16:07:44

or check for that condition

dnolen16:07:55

saying it won't work w/ rationals is meaningless

dnolen16:07:58

nothing works w/ rationals

dnolen16:07:59

sorry for the runaround, had to think about the problem a bit more

dnolen16:07:08

it's just not possible to detect that what you wrote will go wrong

dnolen16:07:56

unless of course you saying the above should work w/ specific floating point values and you want some kind of defined behavior

dnolen16:07:05

but the example you're giving is really muddying the waters about what is desired

pmooser16:07:49

I respect your opinion a lot. I don't think it's accurate to say that I'm asking for doc strings everywhere. I'm asking that things that result in crazy behavior be discoverable as "unsupported" or "undefined" without simply finding that the code doesn't work.

pmooser16:07:03

So what I don't understand is what you think is weird about what I'm doing or why it's unclear what it ought to do -

pmooser16:07:13

It should start at start, and keep adding steps until you exceed end.

pmooser16:07:31

I understand maybe that's not supported with floats, but I'm saying that the expectation I have is simply stated.

dnolen16:07:48

I think you're misunderstanding me

pmooser16:07:56

I must be! I'm trying to understand though.

dnolen16:07:57

I pasted what you pasted above into a REPL

dnolen16:07:05

then I saw something you won't see in ClojureScript

dnolen16:07:12

so how can I possible know what you want

dnolen16:07:23

rationals, some behavior w/ floating point

pmooser16:07:36

You pasted it into a clojure repl? From my standpoint it works fine with clojure, except you get a promotion to 1N at the end of the interval.

pmooser16:07:49

But that's all wrapped up in what clojure does (which appears to be what I would expect), given its numeric tower.

dnolen16:07:00

but that's what I mean

dnolen16:07:03

I can't read your mind

dnolen16:07:16

what prints ain't gonna print in CLJS

dnolen16:07:24

so I don't know what you want

pmooser16:07:36

I'm still confused. Let me try to elaborate.

dnolen16:07:11

(0.0 0.11111111 0.22222222 0.33333334 0.44444445 0.5555556 0.6666667 0.7777778 0.8888889 1.0)

dnolen16:07:27

is the result of (map float ...) your expression above - was that your expectation?

dnolen16:07:37

(in Clojure)

pmooser16:07:55

That would be ideal, down to whatever differences are caused by JS' numeric precision.

dnolen16:07:05

if that's the case, then actionable

dnolen16:07:19

but it wasn't clear to me it wasn't tied up w/ other stuff

pmooser16:07:12

What I was confused by is that range has the wrong number of items, and if you convert it to a vec, the last (incorrect) item disappears, and if you call butlast on it, you get a range that is 2 shorter (instead of merely one). It's a bit difficult to explain because the problem sort of vanishes if you start to manipulate the range in certain ways, so it's a bit challenging at the REPL to try to come up with great pastable examples.

pmooser16:07:58

In my cljs repl, that range has an additional item at the end, 1.1111111111111112 , meaning there are 11 items total, with the last one being obviously far past 'end'.

dnolen16:07:01

not blaming you

pmooser16:07:20

No, surely not - I'm just sorry I'm having trouble explaining it clearly enough.

dnolen16:07:32

but it would have been clearer if you had said "I don't care about numeric tower details"

dnolen16:07:46

and the example is fabricated not important

pmooser16:07:54

Ok, I'll try to keep that in mind when asking questions in the future.

dnolen16:07:09

numeric tower questions are quite common sorry

pmooser16:07:29

I understand, having been bitten by them many times in the past. 🙂

dnolen16:07:04

one second the I think issue is more subtle than this anyway

pmooser16:07:30

Of course, take your time.

dnolen16:07:43

really the bug doesn't have anything do w/ vector though, far as I can tell the range should have only 10 elements not 11

pmooser16:07:23

Sure - that's just additional information (the vector part). I was just surprised that given that the range has the wrong size, that it corrects itself when you do certain things.

pmooser16:07:41

In any case, thank you very much for taking the time to understand this, and I'll try to make sure I am as clear as possible in the future.

dnolen16:07:58

no worries

dnolen16:07:03

that's probably a easy one to fix

dnolen16:07:15

you're more than welcome to take a look at it too

dnolen16:07:19

Range isn't that complicated

pmooser16:07:25

I will take a look if I can.

mfikes17:07:43

Ouch. That range problem is probably impossible to fix while preserving perf in all cases. In Clojure, the result of range is not countable? which is, given the example above, probably the right answer.

alexmiller17:07:31

I think it is sometimes countable

mfikes17:07:09

Ahh, right.

alexmiller17:07:16

specifically the optimized all-longs case backed by LongRange

mfikes17:07:17

ClojureScript could go that route too... would seem to need two different Range

alexmiller17:07:29

which would match Clojure

alexmiller17:07:37

actually 3 if you count (range)

alexmiller17:07:37

which is obviously not countable :)

mfikes17:07:04

We had copied over some aspects of LongRange when adding IChunkedSeq (https://github.com/clojure/clojurescript/commit/345a9b6c966a790aabcfd4bf6a46cb29a86e232d) Maybe more of LongRange would help avoid some of the floating point pitfalls

p-himik17:07:08

The discussion above mentions "Clojure's numeric tower" a few times. What is that? I've never heard about the concept before, and I can't find anything that would describe it.

mfikes17:07:55

Here is an example how ClojureScript's range is hokey compared to Clojure's which uses a sane integer math only when possible:

(let [c (range 0 1 0.1)] [(nth c 6) (nth (vec c) 6)])

mfikes17:07:31

In this case, nth pulls a number out of the range which isn't even in there.

mfikes17:07:06

cljs.user=> (range 0 1 0.1)
(0 0.1 0.2 0.30000000000000004 0.4 0.5 0.6 0.7 0.7999999999999999 0.8999999999999999 0.9999999999999999)
cljs.user=> (nth *1 6)
0.6000000000000001

😱 1
mfikes18:07:28

Solution IMHO is to copy Clojure's approach

dnolen18:07:59

great thanks!