开发者

Adding a validity check dependent on a typeclass (Optional implicits)

开发者 https://www.devze.com 2023-01-29 14:32 出处:网络
In scala, we can use implicit typeclasses to conditionally add methods onto a parameterized type dependent on that type\'s parameters. For example, Iterator.sum:

In scala, we can use implicit typeclasses to conditionally add methods onto a parameterized type dependent on that type's parameters. For example, Iterator.sum:

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

There must be an instance of the Numeric typeclass for this method to even be called:

scala> List(1, 2, 3).sum
res0: Int = 6

scala> List("a", "b").sum
&l开发者_C百科t;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 collection type, My2Col:

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

But I want to mandate that, if this is made with a A : Numeric, then a2 > a1. However, it is entirely valid for it to be made with an A which is not numeric.

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

Has anyone any ideas as to how I might do this?

PS. If anyone has any suggestions for a better question title, I'm all ears


class My2Col[A](a1 : A, a2 : A)(implicit num: Numeric[A] = null){
  for{check <- Option(num); if(check.gteq(a1, a2))}
     throw new IllegalArgumentException
}


I would implement this by creating 2 implicits that represent requirements, one being more general (for all types other than, say, Int or Numeric[T]), and the other being more specific (for Int or Numeric[T]).

Then, following the rules of implicit resolution, I would put the more specific one in the companion object of the Requirement type, and the more general one in the base class of the companion object. This way I would ensure that the compiler tries to apply the more specific one first.

Of course, the downside is that if the user were to explicitly provide the implicit parameter, this mechanism could be circumvented not to do the check.

Something like this (where Int is the type with specific rules, and Foo is the 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)                                                                                                 
    }                                                                                                               
  }
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号