Yuri Geinish
Yuri Geinish

Reputation: 17214

Scala numeric range but not collection

Are there numeric ranges in Scala which are not collections? That is, not for iteration, but for things like "is a number within this range?" or "do these ranges overlap?" etc. I could only come up with the example below but obviously it's very slow:

(1.0f to 999999.0f by 0.01f).view.contains(123123.12f)

Could create my own class for this of course, but it seems so common that I feel there have to be something in Scala already.

Upvotes: 1

Views: 402

Answers (1)

Randall Schulz
Randall Schulz

Reputation: 26486

Spire has an Interval type that will do the things you want.

Addendum

Regarding your question about a more concise syntax for creating instance of Interval. There are a couple of options. One is simply to use import to make the creation a little less verbose:

import Interval.{closed => ci}

val ci1 = ci(1.0, 2.0)

… but that's not much of an improvement.

Another alternative might be to use an implicit conversion from, say, Tuple2[Double, Doublle] to Interval[Double]:

implicit def t2toI(pair: Tuple2[Doube, Double]): Interval[Double] =
  Interval.closed(pair._1, pair._2)

val ci2: Interval[Double] = (1.0, 2.0)

In this case, having to give the type of the val makes this another case of little gain. Hoever, if you're calling a method that takes the Interval[Double] as an argument, the type annotation of the formal parameter to the method is enough to trigger the implicit conversion.

I don't actually know much about Spire, but if there's only one Interval some internal state reflecting the difference between open and closed endpoints, then you have the problem that only one implicit conversion from Tupel2[Double, Double] to Interval[Double] can be in scope so you have lost control over creating any of the other three combinations of open and closed endpoints. Actually, now that I think of it, it doesn't matter how Spire represents intervals, unless the input to the implicit conversion bears the information about what kind of interval to create, the problem holds. You could add more slots to the tuple to carry the endpoint open / closed distinction, I suppose. Again, you lose some in conciseness and it's never as elegant as the notation used mathematical texts.

Because both parens and square brackets "belong to" the language spec, the conventional mathematical notation will never be available for a classic internal DSL.

Lastly, and possibly the best solution, you could create a string interpolator that would enable you to write things like this:

val i1 = i"[1.0, $end)"
val i2 = i"($begin, 4.0]'

etc. Again, this is something I don't have any direct experience with yet, but I believe the API for creating new interpolators is pretty straightforward.

Upvotes: 5

Related Questions