I would like to generalize the Vector class to add some basic math features. I have done this for Vector[Scalar] (based on my Scalar class), a portion of which is shown below. How can I generalize this to also work for Vector[Int] and Vector[Double] without repeating the whole implementation? I am using Scala 3. Thanks.
I’d still like to know how to generalize this implicit class to handle any numeric type that has basic mathematical operators such as +, -, *, /, etc. When I try to generalize it as
implicit class Vectorx[A](self: Vector[A]):
the compiler tells me that type A does not support those operators. I looked for a trait, but there does not seem to be one. Is there a way to limit the type of A to a discrete set of types, such as Int, Long, Float, Double, etc.? I am using Scala 3. Thanks.
You could specify a subtype for each primitive numeric type, and maybe one for BigInt. There aren’t that many, and maybe you don’t need them all? Maybe Double and Long or BigInt is all you need?
I used to think, in the past, that there should be a trait Number, which provides basic math operators, and from which all primitive numbers inherit.
I have since changed my mind, since the practical use of such a super-type is very limited.
Although all the numbers have basic math, they behave differently. Division by zero throws an exception for integers, but returns a special value for floating points. And 3/2 is very different from 3.0/2.0.
Overflow is different. Integers wrap around, while floating points result in special values.
So, what’s left? Matrix multiplication can be written the same for integers and floating points, but for integers, you can run into overflows very quickly, so maybe you will need a BigInt anyway? For more serious linear algebra, like matrix inversion or finding eigenvalues, I don’t think one can use the same algorithm for integers as for floating points.
So, you can try to generalize over all primitive numeric types, but it may be a lot of work for relatively little gain.
Thanks for the reply. The generalized numeric Vector is not something I really need. I was really just curious about it and wondering if I was missing some simple little trick.
I’m not sure what you mean by “specify a subtype for each primitive numeric type.” Can you elaborate on that? You are right that I don’t need very many. I’d settle for Int, Long, Bigint, Float, Double, and perhaps BigDecimal.
If you really want this, in spite of the drawbacks and limitations @curoli has listed, @BalmungSan’s advice sounds just right. Based on Numeric (in Scala 2):
I was hoping somebody would bring up Numeric so I wouldn’t have to
A few caveats about Numeric:
It wasn’t created as a full-on effort to really fully support abstracting across numeric types. It’s more a minimal thing that was made to support collections methods such as .sum, .product, and .mean. So it has basic stuff but depending on what you’re doing you may hit a wall.
In high-performance contexts where you’re crunching tons of data and every cycle and every byte counts, routing operations through the typeclass might be unacceptable performancewise. Of course in many applications it doesn’t matter at all.
Uh, maybe? Not sure if anybody has benchmarked that. I probably should have used some more cautious wording like “consider a possible performance impact, depending”. Would love to learn that I was too pessimistic on this point.