0

我正在尝试创建一个可以将距离从一个单位转换为另一个单位的库。理想情况下,我能够以一个单位指定距离,当传递给需要不同单位的方法时,让 scala 编译器自动转换它。这是我到目前为止所拥有的:

abstract class BaseUnit(scale: Option[Double] = None) {
  def unit: String

  def scalingFactor: Double = scale match {
    case Some(factor) => factor
    case None => 1.0
  }
}

object Cm {
  implicit def inch2cm(inch: Inch):Cm = new Cm(Some(0.393 * inch.scalingFactor))
}

class Cm(scale: Option[Double] = None) extends BaseUnit(scale) {
  def unit: String = "cm"
}

object Inch {
  implicit def cm2inch(cm: Cm):Inch = new Inch(Some(2.54 * cm.scalingFactor))
}

class Inch(scale: Option[Double] = None) extends BaseUnit(scale) {
  def unit: String = "inch"
}

class Distance[A <: BaseUnit](val scalar: Double, val unit: A) {
  override def toString: String = (scalar*unit.scalingFactor)+unit.unit
}


def foo(x: Distance[Cm], y: Distance[Cm]): String = x.toString()+","+y.toString()

在没有明确说明类型参数的情况下使用它似乎使 Scala 使用该Nothing类型:

val a = new Distance(10, new Inch)                                         

println(foo(a, a))                                                               

> scala test.scala

 found   : this.Distance[Nothing]                                   
 required: this.Distance[this.Cm]                                   
Note: Nothing <: this.Cm, but class Distance is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)                   
println(foo(a, a))                                                  
            ^                                                       
one error found     

遵循编译器的建议会导致 foo 返回10.0inch,10.0inch而不是预期的3.93cm,3.93cm.

如果我明确指定类型,编译器会发现差异,但仍然不会隐式地将一个转换为另一个。

val a = new Distance[Inch](10, new Inch)

println(foo(a, a))                      

// found   : this.Distance[this.Inch]    
// required: this.Distance[this.Cm]      
//  println(foo(a, a))                     
//              ^                          
//  one error found                        

我做错了什么,还是编译器不允许这种隐式转换的使用?

4

1 回答 1

1

你只需要

class Distance[A <: BaseUnit](val scalar: Double, val unit: A) { ... }

so that the compiler has a reason to not make A too specific. Otherwise it's free to choose Nothing since it's not related to anything that you're doing.

Also, you know how to convert between units, but you haven't taught it how to convert between distances. You can:

implicit def convertDist[A <: BaseUnit, B <: BaseUnit](da: Distance[A])(implicit a2b: (A => B)): Distance[B] = new Distance[B](da.scalar, a2b(da.unit))

or something like that. (As you define it now, the conversions are backwards, incidentally.)

于 2013-03-30T15:31:14.420 回答