Open kgryte opened 1 year ago
can you please assign me this?
@Planeshifter Have you had the opportunity to review this RFC?
The proposal seems well-considered to me, with the exceptions being reasonable to retain ndarray
behavior where expected.
Three suggestions we discussed:
ndarray
to a scalar (e.g., ndarray2scalar
) which only works for 0D arrays and throws otherwise.NaN
case, we can update @stdlib/assert/is-nan
to handle 0D arrays correctly.ndarray2scalar
to avoid potential "mutation" issues
Description
This RFC proposes supporting
valueOf()
for primitive coercion of zero-dimensional ndarrays. Adding primitive type coercion would allow zero-dimensional ndarrays to exhibit scalar-like behavior in common unary and binary operations, such as addition, subtraction, etc.A common design principle among ndarray APIs is (and will be) returning ndarrays, even when a scalar might be expected (e.g., computing the sum over a one-dimensional
ndarray
). This follows principles set forth in the Data APIs Standard, as consistently returning ndarrays is more conducive for whole-graph optimization, retaining dtype information, and ensuring that data can remain on device (e.g., GPU/TPU), thus avoiding unnecessary device synchronization.Without
valueOf()
support, zero-dimensional ndarrays exhibit the following behavior:By adding
valueOf()
behavior, this RFC proposes the following behavior:Making this change will allow zero-dimensional to (mostly) behave like their scalar equivalents. The exceptions are as follows:
zero-dimensional ndarrays are mutable, while number primitives are immutable. This could lead to some surprises (i.e., action-at-a-distance) if a zero-dimensional ndarray is shared across contexts, as number primitives are passed by value, while ndarray objects are passed by reference. In general, this could be addressed by exercising good hygiene and always converting a maybe 0-D
ndarray
to a number primitive if one believes that a value could be an ndarray and may be shared across contexts.typeof
: a zero-dimensional ndarray will still have anobject
type. Hence,typeof 3.14 !== typeof x
. This could be potentially problematic for those functions which perform explicit type checking of input arguments (e.g.,if ( typeof x === 'number' ) {...}
). Most (possibly all) "base" special mathematical functions assume numeric input and eschew explicittypeof <number>
checks, so passing a zero-dimensionalndarray
to low-level math functions should just work. How likely users are to mix high level ndarray APIs with low-level math APIs remains uncertain. If we wanted to be overly cautious, we could recommend to always do+x
orx.valueOf()
prior to passing a result which may be a zero-dimensionalndarray
to a lower level math function (or any other function explicitly expecting a numeric value).toString()
: a zero-dimensional ndarray will still serialize to anndarray
creation string. This ensures consistency with non-zero-dimensional ndarrays and makes sense from the standpoint that a zero-dimensional ndarray should reconstitute as anndarray
.toJSON()
: similar logic/arguments astoString()
.==
and===
: for the most part, equality matches primitive number behavior. The one exception is for zero-dimensional ndarrays representingNaN
.As can be observed above, the standard check for
NaN
fails, asx === x
compares references, not values. Same for loose equality. In this case, one needs to perform explicit numeric coercionAccordingly, this is a potential footgun, which can be resolved in one of two ways: (a) we can add logic to, e.g.,
@stdlib/math/base/assert/is-nan
to perform numeric type conversion before comparison as shown in the previous example and always ensure that we use the package to check forNaN
or (b) punt the responsibility to userland to perform type coercion.Personally, I'm in favor of (b), as I'm not convinced that mixing abstraction levels (e.g., generic high level APIs with low level "base" APIs) is/will be common and all high level APIs which do operate on
ndarray
objects should have logic for appropriately handling zero-dimensional ndarrays (e.g., unwrapping the 0-d value before invoking a base API).Conclusion
In short, this proposal should strike a reasonable balance between allowing zero-dimensional ndarrays to be scalar-like in most cases and retaining ndarray behavior when operating on values across various ndarray APIs. There are subtle differences which do require vigilance in ensuring that one is explicit in terms of value type expectations, which is especially important in ambiguous contexts where a value may only be "scalar-like".
Related Issues
None.
Questions
Other
valueOf()
will continue to returnthis
.Checklist
RFC:
.