Coder-Spirit / php-bignumbers

A robust library to handle immutable big numbers inside PHP applications
MIT License
131 stars 29 forks source link

The Big Refactor (formerly "Add BigReal interface") #32

Open castarco opened 9 years ago

castarco commented 9 years ago

EDIT : This idea is no longer proposed, a new issue will be posted if we arrive to a consensus in this thread.

Because we want to introduce new classes for big numbers (See #15, #27, #28 and #29), it would be necessary to create a BigRealinterface or abstract class (more probably the second one) in order to allow interoperability between those classes.

Currently, the class Decimal have many defined methods, but almost all the methods use the$scale`parameter, which is very specific for this type. Because it's desirable to use type hinting in the methods signatures, and because PHP doesn't allow classical method overliading (as in Java or C#), I propose to do a big refactor with the following steps:

Important Note: I'm talking about BigRealand not about BigNumber because this way we have more flexibility . Number is a very general concept, and may be in the future we want to implement other number types (not only real or complex numbers, but... say finite fields, quaternions or whatever you can imagine).

castarco commented 9 years ago

@punkka , @stefanmajoor I'll be thankful to read what's your opinion on this topic :) .

stefanmajoor commented 9 years ago

Prefix sharable methods are a big no-no I think, as it complicates the interface for the developer too much.

I think we need to define interfaces first, and that the different classes are much more of a fascade than that it really contains logic. I.e. BigComplex::multiply(BigNumer n) can call a class Multiply(BigComplex c, BigNumber n). In this way you can structure the logic, while keeping the simple interface for the developer.

Also a big question what needs to be answered is what happens when you mix the different types. (Does adding a decimal to an Integer always end up in a Decimal, or does it depend on the answer?

castarco commented 9 years ago

The question that then arises is how we ensure encapsulation, since the (for example) Multiply class don't have direct access to the private properties of the operands. Operating at high level usually adds a lot of CPU and memory overhead because intermediate objects creation and destruction (this is more evident with algorithms with a lot of intermediate computations, like number computations via series).

I think It will be difficult to design a clean API and at the same time keep low resources usage. It's clear that Decimal is too complex at this moment, and adding more methods (as I proposed) wont solve this. My reasons are different than yours (I don't think prefixed methods are complicated for the users of the library, but I think it implies a bloated class).

Your idea of extracting parts of the logic to other classes is a good one, but I'm not sure how to apply it.

I've posted a question in StackExchange. I hope someone enlightens me ^^U : http://programmers.stackexchange.com/questions/278380/how-to-avoid-big-class-complexity-while-keeping-encapsulation

castarco commented 9 years ago

Hi again, I've thought a lot on this issue, and I think I have a "solution", based on the ideas of @stefanmajoor . The idea adds overhead, but I think it will add also a lot of flexibility and potential to the library. I think the trade-off is worth.

What about adding Function classes? I'm not talking about static methods, but typical methods on instances. It may seem weird, but this allow function composition and many other theoretical artifacts (like expressions, limits, integrals, differentiation...), and we can handle a "default" instance in a static property because the instances must/should be stateless. I'm not talking about making the constructor private since I prefer giving the flexibility of creating many function instances (mainly for testing purposes).

The function classes may have a common compute method. This isn't exactly true, because we need to handle the n-arity of function arguments, and again, type hinting. Anyway, in this case creating type-specific methods isn't a big problem since the number of methods of the class will be very small, so it won't be as complex than if we try to do the same thing inside the number classes.

Probably Function must be subclassed depending of the arity of its arguments, since we don't have parametric classes in PHP (like C++ templates, or Java Generics), we'll also need "manual" subclassing to ensure type-correctness.

Initially I wanted to do something like this in another separated repository (which I called MagicTheorist), but I'm not sure how to handle it now.

codisart commented 9 years ago

Hi,

Sorry for the delay but I'm looking to change job and it's taking a lot of my time. I'm not sure to fully understand your idea.

Could-it be possible to shematize or to diagram your solution ?

castarco commented 9 years ago

Hi @punkka ,

This days I haven't enough time to develop this library :( . But finally my idea is to keep only the Decimal numbers, and maybe (but only maybe) also Float, (and remove all the code related to infinite numbers).

This two classes (Decimal and Float) share a common feature: they serve as approximations, not to exactly represent numbers. Is because this that by default I don't "simplify" the trailing zeros in the representations, because it's possible that those zeros are there to indicate the precision of a previous measure (If I have 3.61, what does it mean? exactly 3.61 or a number between 3.610 and 3.615? If we know for sure that the following digit is a zero it's desirable to state it in our representation).

This leads us to another problem, we haven't unique representations for the "same" numbers, and we need to track additional info about the number precision (currently $scale, but this may change), this makes more difficult to program uniform and clean interfaces to operate at the same time with these approximations and with "exact" representations (like integers or rationals) at the same time.

In any case, I think I'll move all the numerical functions outside the Decimal class in order to allow extending the library without burdening the main class.

I want to move all the theoretical stuff to other library "MagicTheorist". This new library will be slower (Is because this that I don't want to implement the theoretical stuff here), but much more flexible.

As a matter of fact, when I started to develop this library, my purpose was to use it in currency handling use cases, and I want to keep this possibility without hurting performance and simplicity.