Nested implicit classes


#1

Hey,

I am a beginner to scala and I stumbled on implicit classes today. When reading about them, I realized I could finally write code like

if (x between 1 and 10) ...

This is my approach:

object Helpers
{
  implicit class IntAnd(left: Int)
  {
    def and(right: Int): (Int, Int) = (left, right)
  }

  implicit class IntBetween(value: Int)
  {
    def between(range: (Int, Int)): Boolean = value >= range._1 && value <= range._2
  }
}

Two problems here:

  1. This way forces me to use unwanted brackets in my code; namely, I need to write 5 between (1 and 10). Is there a way to get rid of these brackets?

  2. This way allows between to accept any tuple of (Int, Int). Is there a way to force between to accept the and function only (not just any tuple)?

Thanks, Simon


#2

Offhand, you could do this by changing the signature of between. Instead of taking a range and returning a Boolean, have that return a new class, which has and as a function on that, and have and do the actual calculation.

In other words, something like:

object Helpers
{
  class Betweener(value: Int, start: Int) {
    def and(end: Int): Boolean = value >= start && value <= end
  }

  implicit class IntBetween(value: Int)
  {
    def between(start: Int) = new Betweener(value, start)
  }
}

Yeah, that works. There might be other options…


#3

There is also syntactic sugar a -> b for a pair, so you can write

implicit class IntBetween(value: Int)  {
      def between(range: (Int, Int)): Boolean = value >= range._1 && value <= range._2
    } 

3 between 1 -> 10 // true

regards,
Siddhartha


#4

Scala also has a built in Range class that is similar to what you’re trying to do (but not exactly the same).

https://docs.scala-lang.org/overviews/collections/concrete-immutable-collection-classes.html#ranges

scala> (1 to 10) contains 0
res0: Boolean = false

scala> (1 to 10) contains 1
res1: Boolean = true

scala> (1 to 10) contains 5
res2: Boolean = true

scala> (1 to 10) contains 10
res3: Boolean = true

scala> (1 to 10) contains 11
res4: Boolean = false

#5

@tmoore: I’ve seen Range some time ago but I guess it slipped my mind. Thank you. However, my question was more about realizing a specific expression in scala.

@siddhartha-gadgil: 1 -> 10 looks really nice. Need to take a closer look at that too. But, as already mentioned, I was actually looking for a possibility to realize a between statement the way I have written it. Thank you though for providing different solutions. To see how scala developers think is a great help when learning this new, beautiful language.

@jducoeur: Thank you! This was the answer I was actually looking for! One small issue bothers me though: Your implementation allows to write 5 between 1 which will return an instance of Betweener. Is there any way to forbid that, i.e. when calling between, one is forced to call and.


#6

To add some background,

x between 1 and 10

is always the same as

(x between 1) and 10

so you either work with that precedence (Mark Waks’ solution), or use operators of higher precedence (Siddharta’s solution) or change the order of things (Tim’s solution)


#7

Yes and no. I don’t see any way to force them to do that, but it seems like it should be an error in-context anyway.

That is, the entire point of this is to get a Boolean, right? If you just say 5 between 1, that expression produces a Betweener, not a Boolean, so it should give a compiler error. (Yay for strongly-typed languages.)

So you don’t have to do anything. See this updated version, which shows the compiler error if you uncomment the bit at the end.