Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TimeSpan has all of the semantics of numbers, but none of the new interfaces #76225

Open
davhdavh opened this issue Sep 27, 2022 · 13 comments
Open
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.DateTime
Milestone

Comments

@davhdavh
Copy link

davhdavh commented Sep 27, 2022

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

namespace System;

public class TimeSpan
        : IComparable,
          IComparable<TimeSpan>,
          IEquatable<TimeSpan>,
          ISpanFormattable,
          ISpanParsable<TimeSpan>,
//NEW
          IMinMaxValue<TimeSpan>,
          IAdditionOperators<TimeSpan, TimeSpan, TimeSpan>,
          IAdditiveIdentity<TimeSpan, TimeSpan>,
          IEqualityOperators<TimeSpan, TimeSpan, bool>,
          ISubtractionOperators<TimeSpan, TimeSpan, TimeSpan>,
          IUnaryPlusOperators<TimeSpan, TimeSpan>,
          IUnaryNegationOperators<TimeSpan, TimeSpan>,
          ISignedNumber<TimeSpan> //<-- problem with this, see below
{
...
}

API Usage

var min = Math.Min(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(1));

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 entire INumberBase<T> is applicable.

@davhdavh davhdavh added the api-suggestion Early API idea and discussion, it is NOT ready for implementation label Sep 27, 2022
@dotnet-issue-labeler
Copy link

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.

@ghost ghost added the untriaged New issue has not been triaged by the area owner label Sep 27, 2022
@hez2010
Copy link
Contributor

hez2010 commented Sep 27, 2022

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.

@ghost
Copy link

ghost commented Sep 27, 2022

Tagging subscribers to this area: @dotnet/area-system-numerics
See info in area-owners.md if you want to be subscribed.

Issue Details

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

namespace System;

public class TimeSpan
        : IComparable,
          IComparable<TimeSpan>,
          IEquatable<TimeSpan>,
          ISpanFormattable,
          ISpanParsable<TimeSpan>,
//NEW
          IMinMaxValue<TimeSpan>,
          IAdditionOperators<TimeSpan, TimeSpan, TimeSpan>,
          IAdditiveIdentity<TimeSpan, TimeSpan>,
          IEqualityOperators<TimeSpan, TimeSpan, bool>,
          ISubtractionOperators<TimeSpan, TimeSpan, TimeSpan>,
          IUnaryPlusOperators<TimeSpan, TimeSpan>,
          IUnaryNegationOperators<TimeSpan, TimeSpan>,
          ISignedNumber<TimeSpan> //<-- problem with this, see below
{
...
}

API Usage

var min = Math.Min(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(1));

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 entire INumberBase<T> is applicable.

Author: davhdavh
Assignees: -
Labels:

api-suggestion, area-System.Numerics, untriaged

Milestone: -

@huoyaoyuan
Copy link
Member

The interfaces was implemented in the first version, but then removed when they were moved into System.Numerics namespace.

@tannergooding
Copy link
Member

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".

@hrrrrustic
Copy link
Contributor

hrrrrustic commented Sep 27, 2022

more concrete scenarios showing the usage scenarios

For #69590 would be nice to have IComparisonOperators<,> on TimeSpan, DateTime, etc

@tannergooding
Copy link
Member

tannergooding commented Sep 27, 2022

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).

@gusty
Copy link

gusty commented Oct 2, 2022

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.

@tannergooding
Copy link
Member

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.

@gusty
Copy link

gusty commented Oct 2, 2022

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.

@tannergooding
Copy link
Member

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.

@JochemPalmsens
Copy link

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)

@ghost
Copy link

ghost commented Nov 12, 2023

Tagging subscribers to this area: @dotnet/area-system-datetime
See info in area-owners.md if you want to be subscribed.

Issue Details

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

namespace System;

public class TimeSpan
        : IComparable,
          IComparable<TimeSpan>,
          IEquatable<TimeSpan>,
          ISpanFormattable,
          ISpanParsable<TimeSpan>,
//NEW
          IMinMaxValue<TimeSpan>,
          IAdditionOperators<TimeSpan, TimeSpan, TimeSpan>,
          IAdditiveIdentity<TimeSpan, TimeSpan>,
          IEqualityOperators<TimeSpan, TimeSpan, bool>,
          ISubtractionOperators<TimeSpan, TimeSpan, TimeSpan>,
          IUnaryPlusOperators<TimeSpan, TimeSpan>,
          IUnaryNegationOperators<TimeSpan, TimeSpan>,
          ISignedNumber<TimeSpan> //<-- problem with this, see below
{
...
}

API Usage

var min = Math.Min(TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(1));

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 entire INumberBase<T> is applicable.

Author: davhdavh
Assignees: -
Labels:

api-suggestion, untriaged, area-System.DateTime

Milestone: -

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api-suggestion Early API idea and discussion, it is NOT ready for implementation area-System.DateTime
Projects
None yet
Development

No branches or pull requests

10 participants