feat: adds unit support to ceil, floor, and fix functions
Description
- adds unit support to ceil, floor, and fix functions -> Addresses #3216
Test
- Added automated unit-tests
Additional Notes
- I wasn't able to run the type tests locally. Can you please verify that they are passing properly?
Is there a restriction that the matrix or array must contain only units? I believe that to be the case. If so, I would like to update the types further. I would like to do it in a separate PR.
Thanks for the updates. I made two more comments, can you have a look at those?
Is there a restriction that the matrix or array must contain only units?
Matrices can contain mixed content of numbers, units, complex numbers etc. There is still an issue in that regard though: the type definitions currently do not allow to have a matrix containing units at all:
https://github.com/josdejong/mathjs/blob/fb76ac419d680543d2ecff70a594e177f3e41b5b/types/index.d.ts#L16
Maybe best to address that issue separately though.
Thank you for the clarification! Is the desired behaviour that matrices and arrays only contain units when the second or third argument to the ceil, floor, fix, or round function is a unit?
For example, round([1], unit('cm')) throws an error, as does round([1, unit('2 cm')], 2, unit('cm')), but that is not reflected in the current type definition.
https://github.com/josdejong/mathjs/blob/6bcb26ca455a1d0f5a0439259c28aef256ed5911/types/index.d.ts#L1192
Proposed new type definition:
round(Unit[] | Unit[][] | UnitMatrix, number | BigNumber, Unit)
round(Unit[] | Unit[][] | UnitMatrix, Unit)
Is the desired behaviour that matrices and arrays only contain units when the second or third argument to the
ceil, floor, fix, or roundfunction is a unit?
That makes sense indeed! Can you refine the type definitions with your proposal?
Is the desired behaviour that matrices and arrays only contain units when the second or third argument to the
ceil, floor, fix, or roundfunction is a unit?That makes sense indeed! Can you refine the type definitions with your proposal?
I played around with it a bit.
This is what I believe the tasks that are required:
- Expand the Matrix and MathArray types to accept Units.
- Add a utility for checking whether an Array is a UnitArray and Matrix is a UnitMatrix
From a user perspective, if they create an array that contains multiple types they will get this error
const array = []
array.push(u1)
array.push(u3)
array.push(1)
array.pop()
math.ceil(array, 1, math.unit('cm'))
They can use the utility (although slow since it has to go through. the entire array/matrix) or assert the type.
if (isUnitArray(array)) math.ceil(array, 1, math.unit('cm'))
or
math.ceil(array as UnitArray, 1, math.unit('cm'))
Thanks for the updates!
The improved TypeScript support is awesome, and with the generic MathCollection<T> we can refine a lot more of the type definitions. However we're already getting out of scope of this PR so let's limit what we do in this PR 😅.
Two questions:
-
There are some subtle changes in the existing type definitions (like for
multiply). Are you sure there are no breaking changes in the types? -
Thinking aloud here: I'm a bit in doubt about the typeguards
isUnitArrayandisUnitMatrixas they are. I think we can use more fundamental helper functions to determine whether a matrix/array contains a single data type (i.e. is homogeneous). Not just for units but for any type. So we would need something more generic likeisHomogeneousArray(array, isComplex)orisArrayOfType(isComplex). The ideal TypeScript notation would be something likeisArray<Complex>(). Maybe we should implement it a method for this onDenseMatrixandSparseMatrixwhich verifies the contents and sets the internaldatatype(which optimizes evaluation) and sets the TypeScript type.In short: this needs some more thinking-through, and maybe it is best to keep that out of scope of this PR and address in a separate discussion. Until then, it would be best not to export new helper functions like
isUnitArrayas long as we're not sure what the final API will be. What do you think?
Thanks for the updates!
The improved TypeScript support is awesome, and with the generic
MathCollection<T>we can refine a lot more of the type definitions. However we're already getting out of scope of this PR so let's limit what we do in this PR 😅.Two questions:
- There are some subtle changes in the existing type definitions (like for
multiply). Are you sure there are no breaking changes in the types?- Thinking aloud here: I'm a bit in doubt about the typeguards
isUnitArrayandisUnitMatrixas they are. I think we can use more fundamental helper functions to determine whether a matrix/array contains a single data type (i.e. is homogeneous). Not just for units but for any type. So we would need something more generic likeisHomogeneousArray(array, isComplex)orisArrayOfType(isComplex). The ideal TypeScript notation would be something likeisArray<Complex>(). Maybe we should implement it a method for this onDenseMatrixandSparseMatrixwhich verifies the contents and sets the internaldatatype(which optimizes evaluation) and sets the TypeScript type. In short: this needs some more thinking-through, and maybe it is best to keep that out of scope of this PR and address in a separate discussion. Until then, it would be best not to export new helper functions likeisUnitArrayas long as we're not sure what the final API will be. What do you think?
Sorry for the delayed response. Very busy this past week.
All of your suggestions sound great. Thank you for the input 😄 ! I will remove the type guards and address your suggestion in a separate PR. I agree that there is definitely more thinking to do.
Regarding the multiply, I will take a look again.
After further look: There are breaking changes, so we will want to see how we want to handle that.
Not all array operations are supported on unit arrays, so if we say that MathArrays can include units. The above code won't capture the first two definitions of the multiply function since it expects MathArray to translate down to MathNumericType[] => but in the current PR, I said that MathArray translates down to MathScalarType[]
multiply<T extends MathNumericType[]>(x: T, y: T[]): T
multiply<T extends MathNumericType[]>(x: T[], y: T): T
So what I can do for now is set the default to a MathNumerticType<T extends MathScalarType = MathNumericType>, but you can explicitly still set it to MathScalarType .
I will address the type guard sometime in the next couple of days.
I did find a small error in one of the multiply function types. Would you like me to file an issue or fix it in one of the upcoming PRs?
Error in function type: multiplication of two MathArrays does not necessarily result in a MathArray.
https://github.com/josdejong/mathjs/blob/07bff5501dae43c1243afc2d9c4b81f70e807098/types/index.d.ts#L1364
To Reproduce Steps to reproduce the behavior.
Multiply two single-dimensional arrays together, and the result is a number.
ad a thorough look at the changes in the type definitions. I expect the changes are not be breaking in normal, practical cases, but there may be tricky edge cases that have breaking behavior. To be careful in this reg
That sounds good. I will address the vector multiplication types separately.
Adding the functionality in version 14 is completely fine with me.
Thank you for the in-depth review 😄
Thanks! Merged now in the v14 branch.
Published now in v14.0.0. Thanks again Orel.