-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Cannot understand the API design of Generic Math INumber.Create[XXXX] methods #62296
Comments
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. |
Tagging subscribers to this area: @dotnet/area-system-numerics Issue DetailsIt seems like the So if I code up new number type public readonly partial struct Fraction : IComparable<Fraction>, IEquatable<Fraction>, ISpanFormattable, IComparable
if FEATURE_GENERIC_MATH
#pragma warning disable SA1001, CA2252 // SA1001: Comma positioning; CA2252: Preview Features
, IMinMaxValue<Fraction>
, ISignedNumber<Fraction>
#pragma warning restore SA1001, CA2252
#endif // FEATURE_GENERIC_MATH
{
// impl not important
} I will implement the public static class Arithmetic
{
// I cannot modify 'int Int32.Create(TOther value)', and
// therefore I cannot convert a Fraction to an Int32 value
// We need both 'TSelf TSelf.ConvertFrom(TOther value)' and TOther 'TSelf.ConvertTo(TSelf value)'
public static int ToInt32<T>(T value) where T : struct
{
if (typeof(T) == typeof(decimal))
{
return (int)(object)((int)decimal.Floor((decimal)(object)value));
}
if (typeof(T) == typeof(Fraction))
{
return (int)(object)(Fraction.Floor((Fraction)(object)value).ToInt32());
}
throw new NotSupportedException("Type is not supported");
}
// This can be done via 'Fraction Fraction.Create(TOther value)'
public static T FromInt32<T>(int value) where T : struct
{
if (typeof(T) == typeof(decimal))
{
return (T)(object)(new decimal(value));
}
if (typeof(T) == typeof(Fraction))
{
return (T)(object)(new Fraction(value));
}
throw new NotSupportedException("Type is not supported");
}
} And want to translate this public static class Arithmetic
{
// I made up the ConvertTo static polymorphic method (that does not exist on INumber<TSelf>)
public static int ToInt32<T>(T value) where T : INumber<T> => T.ConvertTo<int>(value);
public static T FromInt32<T>(int value) where T : INumber<T> => T.Create(value);
} but of course this does NOT compile. How can I solve this using the new generic math interfaces, when I cannot change the Int32.Create (ConvertFrom) code in the BCL? We need something like I think the conversion part of INumber have to be revisited.
|
This is related to the PR feedback that was received when these interfaces were introduced: #54650 (comment) |
+1 from me, this interface just seems like it's repeating the mistakes of the If I invent a new Conversion from any type to any other should be supported whenever at least one type knows a direct conversion in one direction or the other -- so converting between One option might be to introduce the opposite direction Alternatively, it could (after optionally doing any fast-path conversions desired) fall back on using a Either way, though, it's not useful if these methods only work for a limited set of built-in types. On a tangentially-related note, I found it surprising when looking at the implementation of e.g. |
Any setup that is provided directly on the The closest thing to having something that only requires if (TryConvertFrom(value, out var result))
{
return result;
}
if (TOther.TryConvertTo(value, out var result))
{
return result;
}
throw Exception(); These 3 methods would have a strict requirement that
Yes, and the intent is not for them to only be limited to these types. The primitive types are simple and should be directly supported by everything. Slightly more generally, there are APIs on However, even with all of this there is still the issue of two libraries without dependencies on eachother and the only solution for this is to allow some user provided converter via a
This pattern does not actually box. .NET specializes value type generics and so all of the irrelevant paths are optimized away as dead code. Likewise the pattern of |
I agree with all of that. But I still think that this would be an improvement over the current implementation.
Yes, as you said, at least one of the types must know about the other type and agree to the conversion, or it must be the user that explicitly invokes conversion via an intermediate type that both know about (or potentially some longer chain thereof, which is riskier, but at the user's choice). It absolutely must not try to use a third type to convert on its own (not even Having a user-supplied policy interface is not an option I had considered, and I'm not sure how feasible it would be in practice to get it passed around to the places it would need to go, nor how useful it would be in practice. By "user code" I was just meaning the code actually calling
Good to know. |
Actually now I have an even more basic confusion. Let's say I have a
This seems quite messy, but there doesn't appear to be any other way to get there... is there some reason that these are hidden? Or that there doesn't seem to be any syntax for calling explicit static methods? (I also ran into this problem, but I think that's straying a bit off-topic.) |
@tannergooding has this issue been addressed with the latest API changes? |
The methods primarily exist to support usage from a generic context. If you have or know a concrete type, then you should use those directly.
It should be, yes. Any additional issues or concerns should be tracked in a new issue. |
Can you provide a link with more info on these changes?
There is still a generic context. I want to convert from a generic number to a non-generic number. There does not appear to be any way to do so except by writing and calling a separate two-generic-type method -- it's not possible to call any kind of conversion function even from only a single-generic-type context. |
It seems like the
TSelf TSelf.Create<TOther>(TOther value)
(and friends) are what we can call ConvertFrom (in TypeConverter lingo). And there are no equivalent ofTOther TSelf.ConvertTo(TSelf value)
.So if I code up new number type
I will implement the
Create
static method (and friends) (supporting whatever other numeric type I choose). But if I need to use thisFraction
type somewhere else generically (in places where other number types can take its place, aka polymorphic programming), and need something like the following old hacky generic operators helper/util classAnd want to translate this
Arithmetic
helper into the new generic math preview feature in .NET 6but of course this does NOT compile.
How can I solve this using the new generic math interfaces, when I cannot change the Int32.Create (ConvertFrom) code in the BCL?
We need something like
TOther TSelf.INumber<T>.ConvertTo(TSelf value)
, that I can support on my own (non BCL) number type.I think the conversion part of INumber have to be revisited.
The text was updated successfully, but these errors were encountered: