What's needed?
We should not allow to use a plan Quantity without any associated... quantity, as that's just a float.
Proposed solution
Make Quantity a ABC.
Use cases
No response
Alternatives and workarounds
No response
Additional context
The solution is probably much more complex than that, as I believe we allowed to construct "abstract quantities" to overcome complications with the type system, in particular related to Samples, that sometimes need to have a float value and sometimes a Quantity.
I've been investigating different approaches:
-
Making Sample type parameter bound to numbers.Real. This didn't work because brilliantly, isinstance(1.0, Real) is True, but using T = TypeVar("T", Real) is not good enough to the pass float as T (the types don't match). This seems to be a know bug that will not be fixed, as it seems numbers were created before type hints to solve some other problem. In any case, it will be also very hard to make Quantity be a proper Real, because we can't allow all operations (for example, if we multiply a quantity by the same type of quantity, we get quantity², not quantity, so we can't generally allow it.
-
Building our own RestrictedFloat type, or something like that, to make it compatible with float. This sort of works, but when looking at how much restricted a float needs to be to be a Quantity, there is not much left. It is basically comparison, addition and scaling.
-
Making Quantity convertible to float (implement __float__()). This seems to be the most sensible solution. By doing this the most important operations in math related to floats that we need to do in the most generic places even work without conversion, like math.isnan() and math.isinf(), so we can treat Quantitys as float directly for that and remove the isnan() and isinf() methods, being able to work with float and Quantity in a generic way. For other math, we can do v = float(quantity) and then do whatever math we need with v in a generic way. Of course we generally don't want users to convert to float, this is why we have all the as_xxx() methods, but when used generically, like in the resampler, we know we are working with the same type of quantity always, so it is safe to convert to float. Also making operations that escape the quantity (like v * v), should not make sense in resampling functions, because the new sample always need to be in the same quantity as the input samples, so that should never happen because of how math works.
What's needed?
We should not allow to use a plan
Quantitywithout any associated... quantity, as that's just afloat.Proposed solution
Make
Quantitya ABC.Use cases
No response
Alternatives and workarounds
No response
Additional context
The solution is probably much more complex than that, as I believe we allowed to construct "abstract quantities" to overcome complications with the type system, in particular related to
Samples, that sometimes need to have afloatvalue and sometimes aQuantity.I've been investigating different approaches:
Making
Sampletype parameter bound tonumbers.Real. This didn't work because brilliantly,isinstance(1.0, Real) is True, but usingT = TypeVar("T", Real)is not good enough to the passfloatasT(the types don't match). This seems to be a know bug that will not be fixed, as it seemsnumberswere created before type hints to solve some other problem. In any case, it will be also very hard to makeQuantitybe a properReal, because we can't allow all operations (for example, if we multiply a quantity by the same type of quantity, we get quantity², not quantity, so we can't generally allow it.Building our own
RestrictedFloattype, or something like that, to make it compatible withfloat. This sort of works, but when looking at how much restricted afloatneeds to be to be aQuantity, there is not much left. It is basically comparison, addition and scaling.Making
Quantityconvertible tofloat(implement__float__()). This seems to be the most sensible solution. By doing this the most important operations inmathrelated to floats that we need to do in the most generic places even work without conversion, likemath.isnan()andmath.isinf(), so we can treatQuantitys asfloatdirectly for that and remove theisnan()andisinf()methods, being able to work withfloatandQuantityin a generic way. For other math, we can dov = float(quantity)and then do whatever math we need withvin a generic way. Of course we generally don't want users to convert tofloat, this is why we have all theas_xxx()methods, but when used generically, like in the resampler, we know we are working with the same type of quantity always, so it is safe to convert tofloat. Also making operations that escape the quantity (like v * v), should not make sense in resampling functions, because the new sample always need to be in the same quantity as the input samples, so that should never happen because of how math works.