Consider not using `Numeric` for ActiveRecord `#sum`
olivier-thatch opened this issue · 6 comments
v0.13.0 introduced typed signatures for ActiveRecord #sum
so that these methods now return a Numeric
.
While I can see how, on paper, this is an improvement over returning in T.untyped
, in practice it's a bit of an annoyance.
In plain Ruby, Numeric
covers the following types: Float
, Integer
, Complex
and Rational
.
If bigdecimal is required, then the BigDecimal
and Date::Infinity
types are also added to the list of Numeric
descendants. (No clue how or why Date::Infinity
is added when requiring bigdecimal...)
In a typical Rails app, ActiveSupport also adds ActiveSupport::Duration::Scalar
to the list of Numeric
descendants.
The net result is that Numeric
is not a very useful supertype because it covers a bunch of rather heterogenous subtypes, some of which will never be relevant to #sum
(e.g. I don't see how #sum
would ever return a Date::Infinity
or ActiveSupport::Duration::Scalar
, and even Complex
or Rational
seem like extremely fringe use cases).
If we want to use any method that exist on the actual type returned by #sum
(which I expect to nearly always be either an Integer
, a Float
or a BigDecimal
) but not on all Numeric
descendants, we are now forced to cast the result with T.cast
.
This is admittedly subjective and just my opinion, but to me this feels like a worse experience than returning a T.untyped
and being able to call the desired method without casting.
I would suggest either returning to the previous state of returning T.untyped
, or use a new type alias that is less broad than Numeric
(something like SumReturnType = T.type_alias { T.any(Integer, Float, BigDecimal) }
).
I'm going to close this as a duplicate of #1822. Hopefully the associated pull request will improve the experience of using the sum
method with types! Let me know if it doesn't and we can address any remaining issues.
#1830 will fix the issue where sum
is used with a block (which is great, as we also ran into that issue in our codebase), but the non-block signature is unchanged and still returns Numeric
.
Would you be open to changing the return type to something less broad than Numeric
like T.any(Integer, Float, BigDecimal)
? I'm happy to submit a PR if so. Or maybe we can keep the current behavior by default, but make it possible to pass a configuration value to the ActiveRecordRelations
compiler to override the return type? I'm not sure if there's precedent for passing configuration values to Tapioca compilers.
T.any(Integer, Float, BigDecimal)
should work. I think the actual SQL query would return one of those, not a more complex "Numeric" type.
@egiurleo Can this issue be reopened and/or can we get your thoughts on changing the return type to T.any(Integer, Float, BigDecimal)
?
@olivier-thatch I see what you're saying -- apologies for closing the issue prematurely. I'm looking into this now and I'll bring it to the team for discussion on Monday.
Yeah @olivier-thatch please go ahead and open a PR! We'll review.