【问题标题】:Scala implicit Numeric[T] in companion object伴随对象中的Scala隐式Numeric [T]
【发布时间】:2012-05-13 07:17:01
【问题描述】:

我有以下通用 Interval 类(由用户 soc 为我制定):

case class Interval[T](from: T, to: T)(implicit num: Numeric[T]) {
  import num.mkNumericOps // allows us to write from.toDouble and to.toDouble
  def mid: Double = (from.toDouble + to.toDouble) / 2.0
}

典型用例:Interval[Double] 或 Interval[Int]。要添加二元 unionintersection 运算符,我遵循了类似的模式,在伴随对象中使用 (implicit num: Numeric[T])

object Interval {

  def union[T](interval1: Interval[T], interval2: Interval[T])(implicit num: Numeric[T]) = {
    import num.mkOrderingOps // allows interval1.from min
    Interval[T](interval1.from min interval2.from, interval1.to max interval2.to)
  }

  def intersect[T](interval1: Interval[T], interval2: Interval[T])(implicit num: Numeric[T]) = {
    import num.mkOrderingOps
    Interval[T](interval1.from max interval2.from, interval1.to min interval2.to)
  }

}  

在这两种方法中复制(implicit num: Numeric[T])import num.mkOrderingOps 是丑陋的样板。有没有办法在 Interval 对象本身的级别上只执行一次?

【问题讨论】:

    标签: scala numeric implicit-conversion implicit implicits


    【解决方案1】:

    是的,有。

    首先是导入。您可以改为在 Interval 范围内导入 Ordering.Implicits._ 。

    object Interval {
       import Ordering.Implicits._
    
       def union[T](....)(implicit num: Numeric[T]) = {
         // do not import num.mkOrderingOps
         ...
       }
       ...
    }
    

    有了这些隐式,当它找到一个排序操作时,它会在操作发生的范围内寻找隐式排序(数字是一个排序)。在您的每个例程中,恰好都有一个适当的隐含范围。如果你也需要算术运算,也可以导入 Numeric.Implicits._

    现在使用隐式参数。语言中有一个快捷方式,称为上下文绑定: 你可以写def f[T: X](args)而不是def f[T](args)(implicit someName: X[T])

    不同之处在于您没有上下文绑定的隐式名称(您可以使用 implictly[T] 但这几乎不会更短。幸运的是,您不再需要使用 import Ordering 的名称。隐含。_

    所以

    object Interval {
       import Ordering.Implicits._
       // also import Numeric.Implicits._ if you need +,-,*,/ ...
       def union[T: Numeric] ...
       def intersection[T: Numeric] ...
    }
    

    【讨论】:

    • 谢谢。好简洁。我从未见过这种上下文绑定的想法。哪里能学到这样的东西?我读过一本关于 Scala 的书,但我不记得上下文界限。
    【解决方案2】:

    Interval 对象中使用Numeric 类型类有一个类型参数T,它必须绑定在其封闭范围内的某个位置。 Interval 是唯一的常量值,无法提供该绑定。

    解决这个特定问题的一个方法是将unionintersect 操作的定义作为普通实例方法移动到Interval ,在这种情况下它们将共享T 和关联的 Numeric 实例与类的其余部分的绑定,

    case class Interval[T : Numeric](from: T, to: T) {
      import Numeric.Implicits._
      import Ordering.Implicits._
    
      def mid: Double = (from.toDouble + to.toDouble) / 2.0
      def union(interval2: Interval[T]) =
        Interval(this.from min interval2.from, this.to max interval2.to)
      def intersect(interval2: Interval[T]) =
        Interval(this.from max interval2.from, this.to min interval2.to)
    }
    

    但是,如果您希望将这些操作的定义与 Interval 类分开,那么一种减少您需要通过 API 追踪的隐式样板数量的方法是定义您自己的更高级别的类型以 Numeric[T] 表示的类。例如,

    // Type class supplying union and intersection operations for values
    // of type Interval[T]
    class IntervalOps[T : Numeric] {
      import Ordering.Implicits._
    
      def union(interval1: Interval[T], interval2: Interval[T]) =
        Interval[T](interval1.from min interval2.from, interval1.to max interval2.to)
    
      def intersect(interval1: Interval[T], interval2: Interval[T]) =
        Interval[T](interval1.from max interval2.from, interval1.to min interval2.to)
    }
    
    implicit def mkIntervalOps[T : Numeric] = new IntervalOps[T]
    

    使用中的样子,

    def use[T](i1 : Interval[T], i2 : Interval[T])(implicit ops : IntervalOps[T]) = {
      import ops._
      val i3 = union(i1, i2)
      val i4 = intersect(i1, i2)
      (i3, i4)
    }
    

    第三个选项将这两者结合起来,使用隐式定义来丰富原始类和其他方法,

    class IntervalOps[T : Numeric](interval1 : Interval[T]) {
      import Ordering.Implicits._
    
      def union(interval2: Interval[T]) =
        Interval[T](interval1.from min interval2.from, interval1.to max interval2.to)
    
      def intersect(interval2: Interval[T]) =
        Interval[T](interval1.from max interval2.from, interval1.to min interval2.to)
    }
    
    implicit def enrichInterval[T : Numeric](interval1 : Interval[T]) =
      new IntervalOps[T](interval1)
    
    type Ops[T] = Interval[T] => IntervalOps[T]
    

    然后在使用中,

    def use[T](i1 : Interval[T], i2 : Interval[T])(implicit ops : Ops[T]) = {
      val i3 = i1 union i2
      val i4 = i1 intersect i2
      (i3, i4)
    }
    

    【讨论】:

    • 另外,compainon object Interval 是放置隐式转换的好地方。然后,在最后一个使用 use 方法的示例中,您只需要提供 Numeric 上下文边界 use[T: Numeric](i1: Interval[T], i2: Interval[T])
    • 谢谢迈尔斯。我的磨坊有很多谷物!
    猜你喜欢
    • 2014-10-24
    • 2016-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多