Why is the definition of arithmetic + and - mostly duplicate code?

I went through Faro's insides and noticed that the definition of arithmetic + and - looks very similar:

+ aNumber "Refer to the comment in Number + " aNumber isInteger ifTrue: [self negative == aNumber negative ifTrue: [^ (self digitAdd: aNumber) normalize] ifFalse: [^ self digitSubtract: aNumber]]. aNumber isFraction ifTrue: [^Fraction numerator: self * aNumber denominator + aNumber numerator denominator: aNumber denominator]. ^ aNumber adaptToInteger: self andSend: #+ 

and

 - aNumber "Refer to the comment in Number - " aNumber isInteger ifTrue: [self negative == aNumber negative ifTrue: [^ self digitSubtract: aNumber] ifFalse: [^ (self digitAdd: aNumber) normalize]]. aNumber isFraction ifTrue: [^Fraction numerator: self * aNumber denominator - aNumber numerator denominator: aNumber denominator]. ^ aNumber adaptToInteger: self andSend: #- 

As I see it, this completely contradicts the OO-way of designing things and, as a rule, is bad. Why does no one find a better solution?

+5
source share
2 answers

The simplest thing I can think of:

 - aNumber ^self + aNumber negated 

However, it will cost:

  • create another intermediate LargeInteger or fraction
  • two more messages are sent to complete the operation

What we see here is a tribute to optimization. Continuous optimization is a low-level operation used widely.

There are other things in this code that are not perfect:

  • using isInteger and isFraction can also be replaced by some kind of dual dispatcher
  • digitAdd: and digitSubtract: methods work for integers stored as a sign - a value, not a 2-complement, which is not an obvious implementation detail and deserves comment - or maybe it's better to rename digitAddMagnitude: digitSubtractMagnitude:
+4
source

It's not against OO - the behavior is encapsulated, hiding complex behavior behind a simple API. (or the message protocol, as we call it in Smalltalk).

There is a lot of code duplication. One could apply a layer of indirection and save the code in one common place - but this is an example of the sacrifice of compactness for speed - trading from a memory location for speed. It also trades speed at the expense of OnceAndOnlyOnce , which, in my opinion, is your fundamental problem.

In the first Smalltalk, I used LearningWorks (based on the implementation of ParcPlace, iirc) function - just negated with an argument, and then called the + function. This made the operation - slower than operation + .

There are many potential tradeoffs and optimizations in software development β€” the main ones are memory speed (and vice versa), execution speed for development speed (and vice versa), and the ease of getting something working right now, as opposed to being lightweight to make it work and tuned for a long time.

OO development is usually optimized around optimizing development speed at the expense of execution speed, using high-level languages ​​that are faster to develop, but that work slower; optimization for long-term maintainability compared to immediacy; and using as much memory as possible to squeeze as much performance out of naturally slow code as possible.

This, however, is a generalization. For all these are counter examples, I'm sure.

0
source

Source: https://habr.com/ru/post/1234055/


All Articles