Open davhdavh opened 2 years ago
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.
IMO all types having operator overloads should implement their corresponding operator interfaces. If a type is not number, it only isn't necessary to implement the INumberBase/INumber interface.
Tagging subscribers to this area: @dotnet/area-system-numerics See info in area-owners.md if you want to be subscribed.
Author: | davhdavh |
---|---|
Assignees: | - |
Labels: | `api-suggestion`, `area-System.Numerics`, `untriaged` |
Milestone: | - |
The interfaces was implemented in the first version, but then removed when they were moved into System.Numerics
namespace.
As indicated above, the original design had types like TimeSpan
implementing such interfaces. However, API review decided we shouldn't expose that without more concrete scenarios showing the usage scenarios. That likely includes indicating how such APIs would be versioned.
and for some reason ISignedNumber
assumes the entire INumberBase is applicable.
The interface name implies that it is a number
like type and not just some arbitrary type that has a "sign".
more concrete scenarios showing the usage scenarios
For https://github.com/dotnet/runtime/issues/69590 would be nice to have IComparisonOperators<,>
on TimeSpan, DateTime, etc
For comparison, IComparable<T>
and IEquatable<T>
is often a better choice due to the semantics and guarantees it provides (they are not the same as IComparisonOperators
and IEqualityOperators
, and the functions may return different results -- and do in the case of floating-point).
I think TimeSpan
shouldn't implement IAdditionOperators<_,_,_>
as it's loosely tied to numerics and for instance there is no concept of Unchecked Addition.
Ideally it should implement something more generic like IMonoid<_>
and ISemigroup<_>
, we definitely need those interfaces which are not tied to numerics. Other non numeric types like String
, List<'T>
can profit from these interfaces as well, this will allow to generalize over those operations which have some compelling use cases like accumulative error collection to name the first one that comes to my mind from my day to day work,
Then ideally IAdditionOperators<'t,'t,'t>
should inherit from ISemiGroup<'t>
if we had a kind of selective inheritance mechanism based on generics specializations, I wish we had it.
Regarding IMinMaxValue
I think it makes sense to TimeSpan
.
Ideally it should implement something more generic like IMonoid<_> and ISemigroup<_>, we definitely need those interfaces which are not tied to numerics.
Concepts like monoids
and semigroups
are incredibly difficult to expose. Some systems have tried to define or expose such types/support, but they are only loosely related to the actual mathematical term. This is in the same way that tensors
, vectors
, scalars
, functions
, and other terms are most often only very loosely related to the same mathematical concepts.
Such concepts exist in a purely mathematical sense where you have a theoretical "turing machine" with infinite time/resources. However, in the practical sense no such computer exists. Applications generally run on an imperative machine where such machine has finite resources and finite time. Data is broken up into smaller pieces for easy interaction. Interacting with data can often have side effects and it can imply introduced error in the form of rounding, wrapping, saturating, truncating, or more.
Such systems are typically minimally abstracted over with a basic type system. While the complexity of some type systems can vary, they all have limitations that further restrict what models can be exposed and how easy vs complex it is for others to consume the expose interface.
In the case of semigroup
, an extremely simplified view is that it implies an associative
binary operator. However, in computer programming (a + b) + c
and a + (b + c)
can differ in result. While it minimally holds true for certain types of primitive integers (namely those that are two's complement and provide "truncating" arithmetic), it falls apart for types which saturate, types which round, types which have side effects, etc. Many of these concepts (associative, commutative, distributive, identities, inverses, etc) only exist or hold true part of the time and even then often can't be represented via purely static data.
Because of this, among other reasons, we have no plans to add such interfaces now or in the future. It is not the goal or aim of the BCL to try and provide mathematical abstractions like this. The interfaces we expose take into account the practical limitations and interaction scenarios a developer requires. We also take into account that things like monoids
are a higher level concept that many developers will never have been introduced to and may represent a very high entrance barrier as compared to a concept like IAdditionOperators<...>
.
for instance there is no concept of Unchecked Addition.
Such a concept would make sense for TimeSpan
, however. In general, anything that has a Min/MaxValue
has some concept of "overflow" and therefore what happens when those boundaries are "passed". In many cases users will want/expect such overflow to fault (raise an exception/surface an error). However, there are likewise cases where another behavior is desirable instead. Today, TimeSpan
always overflows but allowing for explicit saturation or wrapping would also be viable and potentially something worth exposing.
Concepts like monoids and semigroups are incredibly difficult to expose.
Well, the solution is easy, let's pick a different name, and don't enforce strict adherence to those properties.
What about ITypeThatSupportsABinaryOperationWhichDoesntChangeItsType
? but please don't put it in Numerics
.
Some systems have tried to define or expose such types/support, but they are only loosely related to the actual mathematical term
I'm totally fine with that, as that's not the goal.
What I propose is not to re-create the abstract mathematical properties, but the practical ones, I mean something that encompass string
, list<'t>
and perhaps Timespan
as well.
Well, the solution is easy, let's pick a different name, and don't enforce strict adherence to those properties. What about ITypeThatSupportsABinaryOperationWhichDoesntChangeItsType ?
You can just use IAdditionOperators<T, T, T>
. Having to specify the inputs/outputs isn't really problematic. It allows maximum extensibility while requiring minimal additional typing.
but please don't put it in Numerics.
There is no reason why these interfaces can't be used elsewhere. They ultimately are very numeric oriented and are hidden away here because direct usage isn't as common. It's primarily a consideration for library/type authors rather than for application authors.
API review already discussed and considered this when deciding to put these interfaces in System.Numerics
I'm totally fine with that, as that's not the goal. What I propose is not to re-create the abstract mathematical properties, but the practical ones, I mean something that encompass string, list<'t> and perhaps Timespan as well.
You're going to need to provide some more concrete examples of what you think is missing and usage scenarios around how you expect such an abstraction to be used/be beneficial.
In the case of string/list/array, most of the functionality is covered by interfaces in System.Collections
and System.Collections.Generic
.
Maybe also add IDivisionOperators<TimeSpan,double,TimeSpan>
and IDivisionOperators<TimeSpan,TimeSpan,double>
, etc? To expose more of the mathematical operations?
would improve usability in constrained generic methods. I do understand @gusty 's concern. However, the current implementation is just too limited, and requires overloaded methods that contain duplicate code (= not very clean)
Tagging subscribers to this area: @dotnet/area-system-datetime See info in area-owners.md if you want to be subscribed.
Author: | davhdavh |
---|---|
Assignees: | - |
Labels: | `api-suggestion`, `untriaged`, `area-System.DateTime` |
Milestone: | - |
FYI NodaTime is adding generic math interfaces to their types (https://github.com/nodatime/nodatime/issues/1693).
Background and motivation
TimeSpan is fundamentally a long, and has most of the semantics of being a long. Yet, when adding the
IMinMaxValue<T>
,INumberBase<T>
and so on, none of them were applied to TimeSpan. This prevents making things like Math.Min (if it had an overload that used the new interfaces) work on TimeSpans.API Proposal
API Usage
Alternative Designs
No response
Risks
A few semantics of the new interfaces does not really make sense for TimeSpan, e.g. TimeSpan can be negative, but the concept of NegativeOne does not really make much sense outside being a unary operator. and for some reason
ISignedNumber<T>
assumes the entireINumberBase<T>
is applicable.