arcade icon indicating copy to clipboard operation
arcade copied to clipboard

Floats should be able to be used for basically everything.

Open DigiDuncan opened this issue 4 months ago • 1 comments

[!WARNING] This opinion is contentious, full of joking sass, and I am tired. This may not be my best written issue.

What is a number?

Wikipedia says:

a mathematical object used to count, measure, and label.

Since at least 3400 B.C., we've used numbers to represent values; from amount of oxen to distance to the butcher's hut. And since we've had numbers, we've been able to divide the numbers into pieces smaller than integers or whole numbers.

OK, but what is a Number?

float and int are simply two manifestations for the concept of a Number, an object that represents a value and math can be done to. In many ways, we are repeating the work of our ancestors, defining what math and numbers are. FORTRAN is considered one of, if not the first language to have a distinction between float and int. The reason? floats were harder and more expensive to work with, and operations between the two types were not compatible. This was a consequence of the way programming languages were used; they were very loose abstractions on top of assembly, and of course machine code.

But these distinctions may not always be necessary. For instance, I'd be really upset if I asked for half of your five candy bars and was given two.

Higher-level programming languages already often hide the difference between these two types of number. In fact, JavaScript, Ruby[^1], and Lua all have a single Number type used for all mathematical operations. So, what can we do?

So, What Can We Do?

Well, Python already kinda has an answer. PEP-484/The Numeric Tower acknowledges the interoperability of float and int, and says that functions that take a float should be able to take an int. So, problem solved, then, right? Well...

Enough Yapping, What's The Issue?

Pyglet makes the assumption that basically everything should be int. Position, size, radius, you name it, it's typed as int in Pyglet. And at first blush, that makes sense: why would you need a position that's half a pixel?

Why You Would Need A Position That's Half A Pixel

Honestly, several reasons. The simplest one that comes to mind is animation. Say I want to smoothly lerp[^2] a sprite 100 pixels to the right in one second. My game runs at 60FPS (on a good day), so it seems trivial to figure out how much it needs to move each frame, right?

$$\Delta y = y\frac{t}{f} = 100\frac{1}{60} \approx 1.66666\ldots7$$

Uh-oh! We got a fractional number. If we round up, we'll get there too fast. If we round down, it'll take too long. If we do some fancy thing where we move 2-2-1 every three frames, the animation will look stuttery and bad. This isn't even the only case for when we'd want this. Centering, layouting, accuracy in scale representation, the list goes on. How can we solve this? Well, in the mid-1970s--

[gets glares from the audience]

OK, fine, fine. It's anti-aliasing. The crazy thing? Pyglet already supports it.

Wait, Really?

Go ahead, try it. Pass a float into anything that wants an int in Pyglet. A position, a size, a radius, go wild. Ignore the squiggly lines telling you you're stupid. In the vast majority of cases, Pyglet shrugs it's shoulders and trudges along with no issue.

So, What Are You Proposing?

Here's my pitch: lie. Tell the type checker that Sprite positions and sizes, circle radii, Text positions, font size, whatever, can all be float | int. Pyglet doesn't actually care (and there's a case to be made that this is a Pyglet issue, but until they solidify plans for 3.0, I feel kinda bad throwing this wrench at them.) For now, we #type: ignore until the cows come home. But our types not throwing a bunch of squiggles at everyone with the gall to use math to animate a Sprite feels worth it.

Thanks For Reading

I'm realizing this post is full of snark, but it's not directed at Arcade team or Pyglet team. They both do amazing work and my frustrations do not lay with them. My frustrations lay with type checkers and with the concept of numerical types in general. Big love to all those who see this issue, and thanks for reading my ramblings.

[^1]: It looks like Ruby has Integer and Float, but they seem interoperable and rarely used. [^2]: linearly interpolate

DigiDuncan avatar Aug 28 '25 02:08 DigiDuncan

Disclaimer: Comment written as an outsider user of Arcade. It doesn't represent the opinion of the maintainers in any way.

Types provide guarantees. If Pyglet only says it accepts ints, it means it only guarantees that it will do the right thing for ints. It may, at any given point in the future and even in a patch version, break code that gives it values of types it did not guarantee were fine.

If Arcade starts providing more guarantees than Pyglet provides, it could get stuck in the future. If Pyglet publishes a new version that accepts fewer floats in practice, even in a patch version, Arcade may never be able to upgrade to that newer version. This is not a good place to be in.

Pyglet doesn't actually care (and there's a case to be made that this is a Pyglet issue, but until they solidify plans for 3.0, I feel kinda bad throwing this wrench at them.)

This would be the correct path forward. If Pyglet is willing to provide additional guarantees for all future versions by broadening its end of the contract, then Arcade can take advantage of it. If they are not, then Arcade shouldn't pretend to give guarantees that it has no way of enforcing in the future.

sjrd avatar Aug 28 '25 17:08 sjrd