What is the use case of Integral[T] trait in Scala?

I was looking into NumericRange class in Scala. And I found that every floating point number has to be converted into BigDecimal to be used in a NumericRange data structure.

This is the stackoverflow question I asked - https://stackoverflow.com/questions/74762743/what-is-the-difference-between-generating-range-and-numericrange-in-scala

As the answer suggests, BigDecimal values consists of Integral[T]s, while other floating point numeric types do not.

But my question is what is the use case of Integral[T] in Scala? What does it means for a number to be integral?

I tried to find the answer on my own, and most sources say integral numbers are similar to integers. But it seems like that’s not the case in Scala. Please help me.

You are correct, integral numbers are whole numbers, and BigDecimal doesn’t qualify as an integral number. Looks like this is just cheating, precisely in order to make numeric ranges applicable with BigDecimal. I don’t know the reasoning behind this design choice, but it feels fishy.

While other proper Integral impls are named XYIsIntegral, the BigDecimal one is called BigDecimalAsIfIntegral, annotated with a comment

// For BigDecimal we offer an implicit Fractional object, but also one
// which acts like an Integral type, which is useful in NumericRange.
1 Like

It says scala/src/library/scala/collection/immutable/Range.scala at v2.13.8 · scala/scala · GitHub

  // BigDecimal uses an alternative implementation of Numeric in which
  // it pretends to be Integral[T] instead of Fractional[T].  See Numeric for
  // details.  The intention is for it to throw an exception anytime
  // imprecision or surprises might result from anything, although this may
  // not yet be fully implemented.
1 Like

@LSampath

Here’s the documentation NumericRange It says

NumericRange is a more generic version of the Range class which works with arbitrary types. It must be supplied with an Integral implementation of the range type.

If we look at the source code here for NumericRange scala/src/library/scala/collection/immutable/NumericRange.scala at v2.13.8 · scala/scala · GitHub we can see that quot is used, which means it has to be provided. (Interestingly rem is not used! At least not there. Probably used in other places that require Integral[T].)

As you can see here from the initial part of the code

@SerialVersionUID(3L)
sealed class NumericRange[T](
  val start: T,
  val end: T,
  val step: T,
  val isInclusive: Boolean
)(implicit
  num: Integral[T]
)

an implicit value of type Integral[T] must be present for it to work. Implicits of Scala 2 and typeclasses of Haskell (and of Scala 3) both implement the same idea: ad-hoc polymorphism.

So it’s not about a BigDecimal itself being “integer-like”, it’s about the range-like functionality that needs an “integer-like” thing (the implicit value) to work. The quot (and possibly the rem) methods of the implicit value are used in the generation of ranges. If you don’t know/understand implicits or typeclasses this will be confusing for you.

If we keep reading the comments in the source code it says:

Factories for likely types include Range.BigInt, Range.Long, and Range.BigDecimal. Range.Int exists for completeness

So Range functionality is made specially for BigDecimal but not Double. I’m sure there are some reasons for this.

But these are all technical implementation details that we shouldn’t worry about.

You really shouldn’t be thinking too much about this stuff. Especially if you are new to Scala. Just use it, you don’t need to understand how it’s implemented under the hood.

DO NOT waste your time trying to find answers to these kinds of questions on your own. Get the book Programming in Scala, Fifth Edition Resources which explains every type in detail. Or take the online classes Online Courses (MOOCs) from The Scala Center | Scala Documentation I see newcomers keep wasting so much time with web searches, StackOverflows, tutorials etc. Just go straight to the source made by the language creators.

1 Like

@sangamon Thanks for help.

@spamegg1
You are correct. I am a newbie to Scala, coming from Java. Thanks for help. Now I need to look into implicits.

The issue is that if you use a standard Double or Float, then the range length is not guaranteed due to rounding errors. Their was quite a bit of discussion on this.

See for example this thread.

HTHs

1 Like