Adding validation based on type (optional implications)

In scala, we can use implicit typeclasses to conditionally add methods to a parameterized type, depending on the parameters of this type. For example, Iterator.sum :

 def sum[B >: A](implicit num: Numeric[B]): B = foldLeft(num.zero)(num.plus) 

For this method, there must be an instance of the Numeric class for this method:

 scala> List(1, 2, 3).sum res0: Int = 6 scala> List("a", "b").sum <console>:6: error: could not find implicit value for parameter num: Numeric[java.lang.String] List("a", "b").sum ^ 

So far so good. Let's say I want to have some type of collection, My2Col :

 class My2Col[A](a1 : A, a2 : A) 

But I want to point out that if done using A : Numeric , and then a2 > a1 . However, it is quite fair to have it done with A , which is not numeric.

 My2Col("a", "b") //OK My2Col("b", "a") //OK My2Col(1, 2) //OK My2Col(2, 1) //THROW IllegalArgumentException 

Does anyone have any ideas on how I can do this?

PS. If anyone has suggestions for improving the title of the question, I'm all ears

+7
scala typeclass
source share
2 answers
 class My2Col[A](a1 : A, a2 : A)(implicit num: Numeric[A] = null){ for{check <- Option(num); if(check.gteq(a1, a2))} throw new IllegalArgumentException } 
+12
source share

I would accomplish this by creating 2 implicits that represent the requirements, one of which is more general (for all types except, say, Int or Numeric[T] ), and the other is more specific (for Int or Numeric[T] ).

Then, following the rules of implicit permission, I would put a more specific object in the companion object of type Requirement and more general in the base class of the companion object. This way I guarantee that the compiler will first try to apply a more specific one.

Of course, the disadvantage is that if the user must explicitly provide an implicit parameter, this mechanism can be circumvented so as not to perform validation.

Something like this (where Int is a type with defined rules, and Foo is a collection class):

  package ex trait Requirement[T] { def check(a1: T, a2: T): Unit } trait BaseReq { implicit def genericReq[T] = new Requirement[T] { def check(a1: T, a2: T) {println("generic")} } } object Requirement extends BaseReq { implicit object IntReq extends Requirement[Int] { def check(a1: Int, a2: Int) = { println("int") if (a2 <= a1) throw new IllegalArgumentException } } } class Foo[T](a1: T, a2: T)(implicit req: Requirement[T]) { req.check(a1, a2) // whatever `foo` does follows } object Main { def main(args: Array[String]) { new Foo(1, 2) new Foo("S1", "S2") new Foo(2, 1) } } 
+3
source share

All Articles