TE
科技回声
首页24小时热榜最新最佳问答展示工作
GitHubTwitter
首页

科技回声

基于 Next.js 构建的科技新闻平台,提供全球科技新闻和讨论内容。

GitHubTwitter

首页

首页最新最佳问答展示工作

资源链接

HackerNews API原版 HackerNewsNext.js

© 2025 科技回声. 版权所有。

The Numeric Tower Fiasco

17 点作者 tie-in大约 1 年前

8 条评论

raymondh大约 1 年前
Numeric towers are indeed a sore point.<p>* There are more 64-bit integers than C floats&#x2F;doubles. But in math, reals are a superset of the integers.<p>* Because floats are rounded, `x &lt; x + 1`, is not an invariant.<p>* The possibility of a `NaN` value means that the assignment `y = x` does not guarantee that <i>x</i> and <i>y</i> are equal.<p>* Floats are implemented as binary fractions, so they are actually rationals.<p>I disagree with the OP that OOP is entirely flawed. The core ideas of encapsulation and messaging are a really useful organizing principle. And polymorphism beats maintaining giant case-statements.<p>Only when inheritance is added to the mix does it get dicey. As a tool for code reuse, it is not a bad idea. I think the central problem is that people want more from inheritance that it has to give (especially if you expect that children are always substitutable for their parents).
评论 #40483141 未加载
gwbas1c大约 1 年前
&gt; The object-oriented tradition obsesses with encoding is a relations among types as a class hierarchy.<p>That&#x27;s a fallacy. Complicated type hierarchies are a mistake in object oriented programming. There are a lot of good reasons to use inheritance, but it&#x27;s not really something that should be obsessed about.<p>A large &quot;object oriented&quot; program will still declare the large majority of its types without inheritance. (Or with minimal inheritance, SomethingService inherits from ServiceBase, SomethingRecord inherits from RecordBase.)<p>&gt; When I use a tool and get unsatisfactory results, I blame myself for not using the tool correctly.<p>&gt; The approach of piling classes on top of one another and trying to make this stack coherent is fundamentally flawed. It fails spectacularly even on tiny examples where the problem domain is mathematically specified.<p>Yes, you aren&#x27;t using the tool correctly. You can not interchange rational and natural numbers in a computer without conversion. Typically rational and natural &quot;classes&quot; wouldn&#x27;t share a common base class, but might share common-ish interfaces.<p>For example, in C# I could declare a generic INumber interface, and all my number types could support it, <i>but by the nature of being generic, you can&#x27;t interchange a rational and natural without performing some kind of conversion.</i> (INumber&lt;TNumber&gt; where TNumber : INumber) This would allow a programmer to write generic code that handles an INumber, but not a mix of different INumber types. (SomeMethod&lt;TNumber&gt;(TNumber arg) where TNumber : INumber&lt;TNumber&gt;) (I believe newer C# has something like this baked-in, but I haven&#x27;t used it yet.)
vinkelhake大约 1 年前
OOP is a tool like any other and isn&#x27;t always suitable. A similar example from geometry: all squares are rectangles, but you wouldn&#x27;t implement a square type by deriving from rectangle because it breaks LSP.<p>&gt; Adding a new operation on numbers requires modifying all the classes.<p>This is part of the expression problem. With an OOP approach, adding a new operation is a &quot;heavy&quot; task. With a functional approach, adding a new type is a heavy task.<p>In the case of the numerical tower, the set of types is more or less locked down.
评论 #40447023 未加载
GuB-42大约 1 年前
When approaching object orientation, I usually take a low level approach.<p>A class is a data structure.<p>An object is the actual data.<p>A method is a function talking an object as the first parameter<p>A subclass is a data structure containing first the parent class data structure, then the elements specific to the subclass. It means that inheritance is a special case of composition.<p>Virtual methods are function pointers in an object (often with a level of indirection: the vtable).<p>So instead of thinking of inheritance as a &quot;is a&quot; relationship, I treat is as &quot;has (the properties of) a&quot;, because that&#x27;s how it is in memory. For example, we often do stuff like an employee is a person, so employee inherits from person, and adds some extra properties, like pay. But I prefer to think of it as &quot;an employee has the properties of a person&quot;. But we also decide that the &quot;person&quot; properties are special, because these are the properties you will use often, so you use inheritance instead of composition, and doing that, you take advantage of some syntactic sugar and an efficient memory representation.<p>So for that number tower, do you want &quot;integer&quot; to inherit &quot;natural&quot;. Probably not, because while an integer has the properties of a natural with an additional &quot;sign&quot; property, you are unlikely to want to address your integer as a natural, there are few cases where it makes sense, so explicit composition would be more appropriate. In the reverse direction, do you want &quot;natural&quot; to inherit &quot;integer&quot;. Maybe, it depends on how &quot;special&quot; natural numbers are over integers.<p>OOP may be flawed from a mathematical perspective, but it is a good tool for practical programming, that&#x27;s why it is so popular. The &quot;is a&quot; and &quot;has a&quot; relationships are just to help understand a concept that is really about chunks of memory.
readthenotes1大约 1 年前
Funny, because inheritance was the least important aspect of OOP when I did it (in part, because it was the one most abused)
评论 #40446862 未加载
_nalply大约 1 年前
For an RPN calculator I discovered similar problems with a number hierarchy as well. The calculator has four number types:<p>- integers (for hexadecimal operations),<p>- quotients (like some HP calculators),<p>- IEEE 756 floating point numbers (because that is the most simple thing on computers) and<p>- complex numbers based on two IEEE 756 components<p>Mathematically these types are subsets of each other:<p><pre><code> ℤ ⊂ ℚ ⊂ ℝ ⊂ ℂ </code></pre> But number values in a computer aren&#x27;t subsets. Not all 64 bit integers are representable as 64 bit IEEE 756 numbers, for example odd integers beyond 10^53.<p>Hand-held calculators need to hide a lot of complexity to be intuitive.<p>One example. If you have two values in the calculator that approximate e and i * pi as good as the calculator can and request the power operation, then you have the Euler identity and should get the integer 1. The implementation uses complex numbers and because of rounding errors you might get something like 1.0000000003. To be user-friendly, the calculator needs to hide that.<p>That&#x27;s what I did:<p>- Use four distinct types without any object-oriented features<p>- Operations aren&#x27;t methods but functions with a given signature, for example as a Rust function:<p><pre><code> fn sin(c: Complex) -&gt; Complex </code></pre> - Coerce when an operation expects a different type<p>- Don&#x27;t use the whole precision of floating point (truncate to a lower precision and hide one decimal precision digit after truncation)<p>- The default display omits both the imaginary part and the fraction if zero
nerdponx大约 1 年前
&gt; Most people (me included) will instinctively reach for the class structure where Natural is the base class, and Integer extends that base.<p>Huh? Why? That&#x27;s <i>mathematically</i> nonsense, so why would you encode it in your type system?<p>Naturals are a strict subset of Integers. The Liskov substitution principle applies perfectly well when you set this up the right way.<p>And guess what: actual &quot;numerical tower&quot; implementations (yes, even in languages with object systems like Common Lisp and Python) don&#x27;t work this way either.<p>For example, in Python, the numerical tower is literally the opposite of what is proposed here. The base class is Number. Then Complex inherits from Number. And so on down to Integral, which is the <i>most derived</i> number type, not the most basic.<p>The numerical towers in Common Lisp and Scheme behave similarly. They all differ in structure details, but they all work the same way: the largest collections are the most basic types, the smallest collections are the most derived types.<p>This whole article is complaining about OOP being bad, when in reality OOP is helping to reveal a fundamentally bad design, which would be wrong in <i>any</i> programming paradigm.<p>There&#x27;s nothing wrong with Peano numbers of course, but to introduce them in order to avoid fixing your unsound <i>conceptual</i> model, and to claim that it&#x27;s an improvement over the OO-with-inheritance approach, is just silly.<p>It&#x27;s certainly interesting that OO inheritance only supports &quot;subset&quot; relationships and not &quot;superset&quot; relationships, which I think is maybe what the author was originally trying to illustrate. It&#x27;s something we do in math all the time. But that&#x27;s not where the article goes and that&#x27;s not what it presents as its own conclusion.<p>---<p>Edit &#x2F; addendum:<p>You can build up a class hierarchy &quot;additively&quot; (adding structure&#x2F;operations in more-derived types) rather than &quot;subtractively&quot; (removing structure&#x2F;operations in more-derived types) using mixins in OO or type classes in FP.<p>The author mentioned Haskell and went so far as to define a custom ADT for integers but didn&#x27;t notice that Haskell itself has its own kind of numerical tower in its type classes Num, Integral, etc. which works in exactly the <i>additive</i> style that they realized was incompatible with traditional OO inheritance.
评论 #40483194 未加载
riffraff大约 1 年前
&gt; All types in the hierarchy need to know about one another. Adding a new type requires changing all other types.<p>This was solves a few decades ago in smalltalk with the coerce protocol (aka double dispatch).<p>This article feels quite strawmanish.