添加依赖于类型类的有效性检查(可选隐式)

发布于 2024-10-06 05:10:55 字数 937 浏览 3 评论 0原文

在 scala 中,我们可以使用隐式类型类根据参数化类型的参数有条件地将方法添加到该类型上。例如,Iterator.sum:

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

必须有 Numeric 类型类的实例才能调用此方法:到目前为止

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
                  ^

,一切顺利。假设我想要某种集合类型,My2Col

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

但我想强制执行,如果这是用 A : Numeric 制作的, 然后 a2 > a1 。但是,使用非数字的 A 来创建它完全有效

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

有人对我如何做到这一点有任何想法吗?

PS。如果有人对更好的问题标题有任何建议,我会洗耳恭听

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
<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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

执笔绘流年 2024-10-13 05:10:55
class My2Col[A](a1 : A, a2 : A)(implicit num: Numeric[A] = null){
  for{check <- Option(num); if(check.gteq(a1, a2))}
     throw new IllegalArgumentException
}
class My2Col[A](a1 : A, a2 : A)(implicit num: Numeric[A] = null){
  for{check <- Option(num); if(check.gteq(a1, a2))}
     throw new IllegalArgumentException
}
热鲨 2024-10-13 05:10:55

我将通过创建 2 个表示需求的隐式来实现这一点,一个更通用(对于除 IntNumeric[T] 之外的所有类型),另一个更通用更具体(对于 IntNumeric[T])。

然后,按照隐式解析的规则,我会将更具体的一个放在 Requirement 类型的伴生对象中,将更通用的一个放在伴生对象的基类中。这样我可以确保编译器首先尝试应用更具体的。

当然,缺点是如果用户显式提供隐式参数,则可以绕过此机制而不进行检查。

像这样的东西(其中 Int 是具有特定规则的类型,Foo 是集合类):

  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)                                                                                                 
    }                                                                                                               
  }

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)                                                                                                 
    }                                                                                                               
  }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文