I don't think so, I suspect that this is standard fare for the audience of a website called 'lithub.' In the words of gamers: git gud, scrub. (<- light hearted jab)
Nice or not, pretending that double precision floats and arbitrary precision integers can be stacked as a tower is foolish. There are floats that can't be represented as integers, and integers which can't be represented as floats.
This is where you say something about "exact" vs "inexact" as though that will hand wave it away.
> Nice or not, pretending that double precision floats and arbitrary precision integers can be stacked as a tower is foolish. There are floats that can't be represented as integers, and integers which can't be represented as floats.
The numeric tower in Scheme describes general number types with above of in the tower graphic (in the Wikipedia article) meaning subtype of. double precision floats and arbitrary precision integers are representations of numbers. Both would also be Real numbers.
> This is where you say something about "exact" vs "inexact" as though that will hand wave it away.
I'm not familiar with this debate, but how is that a hand wave? The article describes a reasonable-sounding way to extend the tower with a second dimension of precision. Following those rules, you would never just convert between bigint and float, but an expression involving both would output a float.
I wouldn't parade it around as a triumph over the problem, and it's arguably better to require people to be explicit about whether converting the float to bigint, or the bigint to float, is what you wanted.
That doesn't really strike me as worse than any other use of == on a float. If anything needs to change there, I think it's more rigor in float comparisons.
Basically, ULP-level inaccuracy is a problem inherent to having float at all, even without bignum interactions. They would be a menace even if you had a pure tower from 32 bit int to double to complex to more.
I wasn't trying to draw attention to comparisons for equality. Perhaps I should've used an arrow => instead of == to indicate "the result of this operation", but that probably would've caused confusion too...
The real point is that you can get some non-intuitive answers from letting that numeric tower make conversion decisions for you. It's just a rule, and it's not an amazing rule.
> I wasn't trying to draw attention to comparisons for equality. Perhaps I should've used an arrow => instead
Then it's even less of issue. Yes if you convert to a float you get rounding, what did you expect when you introduced a float?
It's somewhat unintuitive but that's the nature of floating point.
> The real point is that you can get some non-intuitive answers from letting that numeric tower make conversion decisions for you. It's just a rule, and it's not an amazing rule.
But again, you can have the same kind of issue without bignums. It's not a tower problem it's a float problem.
The specific type of conversion is one I don't see as a big issue. The programmer deliberately decided to use an imprecise data type for the calculation.
But more importantly, I'm saying that the problematic rounding can occur even if your tower does not have both bigint and float. It can happen even if every layer can completely represent every value of the layer above it. Do you have any complaints that are unique to a tower that has both bigint and float, and don't apply to towers that only have float?
To elaborate on that, an implicit cast directly from a single bigint to a single float won't happen with the rules in the wikipedia article. You'd have to do something like bigint+float, which can have horrible rounding errors, but those horrible rounding errors are also present in float+float.
And you can even have these problems without a tower. So I don't see how the bigint and float scenario is an argument against towers.
From a distance, I kind of like Scheme, so I went and re-read the R5RS section on the topic. To me, the numeric tower (generalized) says:
N < Z < Q < R < C < H
ℕ ⊂ ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ ⊂ ℍ
That's a nice statement about idealized sets of numeric values. So `integer?` implies `rational?` implies `real?` implies `complex?` implies `number?` in Scheme predicates and type conversions.
But no programming language can have "Reals" (they aren't computable), so floats are a common/useful approximation. And in actuality `bigint?` doesn't imply `floating?`, and `floating?` doesn't imply `bigint?`. Neither is a strict subset of the other, and because of this you can easily find examples where implicit conversion does something "questionable". You've made it about rounding errors, but I'm trying to criticize something about pretending they are subtypes/subsets. Claiming it's a tower and hand waving about exact/inexact doesn't make it a tower, and so I think implicit conversion for these is a poor choice.
You can have little subset relations for implicit conversions:
> But it's not much of a tower, and it's more of a collection DAGs.
> I'm trying to criticize something about pretending they are subtypes/subsets. Claiming it's a tower and hand waving about exact/inexact doesn't make it a tower
I thought we established right away that it's not a single tower. The description in the wikipedia page is two towers with links between them. (Or at least it's two if you don't waste effort on things like having both float64 and complex32.)
But I don't see any hand waving. The relationships and conversions are very clear. That's why I interpreted your complaint as being more about the specific operation. So with your correction, I need you to explain where you see hand-waving.
If you just don't like the name "Tower" for an implementation that has both bignums and floats then okay I agree I guess?
> I thought we established right away that it's not a single tower.
Where did we say that? The first picture on the Wikipedia page shows the tower as a linear stack of items from set theory. The Scheme predicates are named similarly. This is the appealing myth.
> The description in the wikipedia page is two towers with links between them.
Not on the page I'm seeing. Are you reading the English page? At the bottom, I see a tree of abstract types (sets).
This shows that you can traverse (Integer to Rational to Real) and (Float to Real) to find the common abstract type Real. But there isn't actually a Real type you can do operations with. You've got concrete BigInt and Float64, and even if Real is implemented as a C-style tagged-union of the two types, you still need to pick one or the other for doing operations like addition. Then the Scheme standard says stuff like, "try to be exact when you can, but inexact is ok sometimes". So all the set theory justification is out the window, and it's really just an ad hoc rule.
It's just not as elegant as it seems, and it gives an unsound justification to making implicit conversions.
> If you just don't like the name "Tower" then okay I agree I guess?
Please don't do that. I've tried to clarify details in response to your questions, but if you're just going to dismiss it with some snarky crap like that then you can go fuck yourself.
Reply if you want, but I'm guessing we're done here.
> Where did we say that? The first picture on the Wikipedia page shows the tower as a linear stack of items from set theory. The Scheme predicates are named similarly. This is the appealing myth.
In the section where the wikipedia page talks about exact and inexact, the specific thing you were calling out, it says "Another common variation is to support both exact and inexact versions of the tower or parts of it; R7RS Scheme recommends but does not strictly require this of implementations. In this case, similar semantics are used to determine the permissibility of implicit coercion: inexactness is a contagious property of numbers,[6] and any numerical operation involving both exact and inexact values must yield inexact return values of at least the same precision as the most precise inexact number appearing in the expression, unless the precision is practically infinite (e.g. containing a detectable repetend), or unless it can be proven that the precision of the result of the operation is independent of the inexactness of any of its operands (for example, a series of multiplications where at least one multiplicand is 0).".
I want to especially highlight the phrase "exact and inexact versions of the tower or parts of it" Which I then reacted to by saying "The article describes a reasonable-sounding way to extend the tower with a second dimension of precision." Once you have two dimensions it's no longer a single tower. I thought that was the common ground that we were talking on, that if you use that method it's not a true tower anymore.
> Please don't do that. I've tried to clarify details in response to your questions, but if you're just going to dismiss it with some snarky crap like that then you can go fuck yourself.
That wasn't snark. I am really trying to understand your argument, because it looks like we've been talking about different things the entire time.
I had thought we established from the very start that the description on the wiki page wasn't actually a single tower. If you are still trying to convince me it's a more complicated graph, then I agree with you, and I don't understand how we got so far without that being clear. Sorry for sounding reductionist about it.
So please, honest question for clarification, do you object to the graph of number types described by that paragraph, do you object to using the word "tower" to talk about it, or do you object to both? Please don't get mad at me for asking, or think I'm trying to dismiss you.
And if someone builds a pure tower that goes int32, double, complex, quaternion, do you think that's inherently self-defeating because it can't live up to the promises of a tower? It doesn't have the issue of floats versus bignums; it's strict subsets all the way down.
I'm sorry for misreading your comment about the term "tower". :-)
> do you object to using the word "tower" to talk about it
No, I don't really care about the terminology, except when it helps to communicate.
> do you object to the graph of number types described by that paragraph
I think the problem boils down to using a flawed analogy to arrive at a conclusion and then pretending the conclusion is sound and elegant. There are really two things going on:
First, we've got a tower, or tree, or DAG of "abstract" types. These are mathematical constructs or Platonic ideals. So you can build a tower that says "All Integers are Rationals" and "All Rationals are Reals". And it's supported by Set Theory! So you conclude that you can use an Integer anywhere that a Rational or Real is allowed. Then, knowing that we're going to apply this to a programming language, you add "All Floats are Reals". Fine, we've got abstract Floats, and it looks lovely.
Second we've got actual "concrete" data types. These are things like Float64, Int32, or BigInt. Importantly, you can't have an implementation of Real anything. In general, Real numbers can't be processed on a Turing machine. You can have a tagged union of Computable things, but that's not really the same as "Real" in bold quotes.
Ok, so the mistake comes when you try to combine those first and second sets of things. We say concrete BigInt is like the abstract Integers, and concrete Float64 is like the abstract Floats. So far so good. Then we look at the abstract tower, we decide that Integers and Floats need to become Real, so we say BigInt and Float64 need to use Reals to get a common type. But there is no common type. We said the concrete types are analogous to the abstract types and made an unsound conclusion.
Finally, we write the compiler, and reality hits us. So we go back to the standard and add some bits about "Some things really should be Exact. Conforming implementations should try to avoid Inexact when they can." It's not a separate tower - it's a bandaid for flawed logic.
Anyways, this is all a bit too philosophical. I'm not actually passionate about it, but our discussion kept going, and you kept asking, so I kept trying to explain. Most people like implicit conversions in their programming languages, and so you've got to make up some rules. I just don't like pretending the rules are not ad hoc, and it's nothing a smug lisp weenie should really be smug about.
> And if someone builds a pure tower that goes int32, double, complex, quaternion, do you think that's inherently self-defeating because it can't live up to the promises of a tower?
Assuming the obvious implementation of complex and quaternion built on two or four doubles, it's fine. Each type represents a set that is a proper subset of the next type in the list.
Annoyingly, it'll all go to crap if you have int64 though.
You gotta read between the lines with the commenter above. Their name is a reference to "Smug Lisp Weeny", and they're part of the religion (cult) that thinks everything in Lisp (usually Common Lisp) is perfect. He couldn't care less about Pike, except as an excuse to be smug about Lisp.
Besides his nick, was he smug? He just noted that Lisp(likes) solve this problem for some version of 'solve' with the numerical tower. The implementations don't (usually; I am not aware of any) mix exact and inexact as that would be foolish obviously.
> Meanwhile heavily hyped successors that lacked it, like Haskell, have vanished without a trace, leaving not even one widely used program in their wake.
Do you think that inheritance had something to do with that?
Cathode Ray Dude on YouTube had a video with a guy calling himself tr0n(?) showing off his Symbolics machine. I can't find the video, but it's out there and it's good.
Most online games use client side prediction, so any input made by the client happens almost instantly on the client and it feels really good, and can be rollbacked if the server disagrees. If you stream your game remote with 40ms it will add 40ms to your input and that just feels bad (not to mention jitter, especially if you're on semi-congested wifi), but its not unplayable or even that noticeable in many games. Would I play some casual Dota like that? Sure. But not high ranked games.
I think Rust's language design is in part to blame, as it does not force the programmer to think sufficiently of the layout of the memory, instead allowing them to defer to a "global allocator".
I never said that C and C++ doesn't suffer from the same design problem? I'd say that Zig is the best in class here, typically forcing you to pass along an allocator to each data structure. C is a bit better than C++, as it uses an allocator explicitly, while C++ relies on new/delete with a default impl calling malloc/free.
Still a language design issue: C++ and Rust doesn't put allocation concerns front and center, when they very much are. Not encouraging thinking about these things is very bad for systems languages.
reply