型クラスでこれを行う方法に興味があったので、無効なRectangleを使用してコードをコンパイルすることさえできないこのバージョンを思いつきました。私はこれが少しきれいにできると思います、しかしこれは私がすぐにまとめたものです:
trait LengthCalc[-A] {
def length(x: A): Double
}
trait WidthCalc[-A] {
def width(x: A): Double
}
trait AreaCalc[-A] {
def area(x: A): Double
}
case class Rectangle[A <: Option[Double], B <: Option[Double], C <: Option[Double]](lengthOpt: A = None, widthOpt: B = None, areaOpt: C = None)
(implicit lengthCalc: LengthCalc[Rectangle[A,B,C]], widthCalc: WidthCalc[Rectangle[A,B,C]], areaCalc: AreaCalc[Rectangle[A,B,C]]) {
lazy val length = lengthCalc.length(this)
lazy val width = widthCalc.width(this)
lazy val area = areaCalc.area(this)
}
implicit object RectLengthCalcFromLength extends LengthCalc[Rectangle[Some[Double], _ <: Option[Double], _ <: Option[Double]]] {
def length(x: Rectangle[Some[Double], _ <: Option[Double], _ <: Option[Double]]) = x.lengthOpt.get
}
implicit object RectLengthCalcFromWidthAndArea extends LengthCalc[Rectangle[None.type, Some[Double], Some[Double]]] {
def length(x: Rectangle[None.type, Some[Double], Some[Double]]) = (for {
area <- x.areaOpt
width <- x.widthOpt
} yield (area / width)).get
}
implicit object RectWidthFromWidth extends WidthCalc[Rectangle[_ <: Option[Double], Some[Double], _ <: Option[Double]]] {
def width(x: Rectangle[_ <: Option[Double], Some[Double], _ <: Option[Double]]) = x.widthOpt.get
}
implicit object RectWidthFromLengthAndArea extends WidthCalc[Rectangle[Some[Double], None.type, Some[Double]]] {
def width(x: Rectangle[Some[Double], None.type, Some[Double]]) = (for {
area <- x.areaOpt
length <- x.lengthOpt
} yield (area / length)).get
}
implicit object RectAreaFromArea extends AreaCalc[Rectangle[_ <: Option[Double], _ <: Option[Double], Some[Double]]] {
def area(x: Rectangle[_ <: Option[Double], _ <: Option[Double], Some[Double]]) = {
x.areaOpt.get
}
}
implicit object RectAreaFromLengthAndWidth extends AreaCalc[Rectangle[Some[Double], Some[Double], None.type]] {
def area(x: Rectangle[Some[Double], Some[Double], None.type]) = (for {
width <- x.widthOpt
length <- x.lengthOpt
} yield (width * length)).get
}
呼び出しの例を次に示します。
scala> Rectangle(Some(3.),None,Some(4.))
res8: Rectangle[Some[Double],None.type,Some[Double]] = Rectangle(Some(3.0),None,Some(4.0))
scala> res8.width
res9: Double = 1.3333333333333333
scala> Rectangle(Some(3.),None,None)
<console>:25: error: could not find implicit value for parameter widthCalc: WidthCalc[Rectangle[Some[Double],None.type,None.type]]
Rectangle(Some(3.),None,None)
scala> Rectangle(None, Some(8.), Some(64.))
res10: Rectangle[None.type,Some[Double],Some[Double]] = Rectangle(None,Some(8.0),Some(64.0))
scala> res10.length
res11: Double = 8.0