Skip to content

Instantly share code, notes, and snippets.

@piyo7
Last active January 12, 2017 06:00
Show Gist options
  • Save piyo7/1eb5d254933ec57f6d42af0d2ea99853 to your computer and use it in GitHub Desktop.
Save piyo7/1eb5d254933ec57f6d42af0d2ea99853 to your computer and use it in GitHub Desktop.
おてがる単位型パターン 〜不正な単位演算はコンパイルエラーにしよう〜 ref: http://qiita.com/piyo7/items/df002e4fa9ec832ea0a2
val imp: Int = 400
val yen: Int = 30
val cpm: Double = yen / imp
val imp: Int = 500
val yen: Int = 30
val cpm: Double = yen.toDouble / imp.toDouble * 1000
val imp: Vimp = Vimp(400)
val yen: Yen = Yen(30)
val cpm: Cpm = yen / imp // コンパイルエラー!!!
val imp: Imp = Imp(500)
val yen: Yen = Yen(30)
val cpm: Cpm = yen.mapDouble / imp.mapDouble
assert(cpm == Cpm(60.0))
implicit val mulImpCpmYen = UnitNum.Mul[Double, ImpTag, CpmTag, YenTag](0.001)
def *[Tag1, Tag2](x: UnitNum[A, Tag1])(implicit op: Numeric[A], mul: UnitNum.Mul[A, Tag, Tag1, Tag2]) =
UnitNum[A, Tag2](op.times(op.times(value, x.value), mul.factor))
def /[Tag1, Tag2](x: UnitNum[A, Tag1])(implicit op: Fractional[A], mul: UnitNum.Mul[A, Tag1, Tag2, Tag]) =
UnitNum[A, Tag2](op.div(op.div(value, x.value), mul.factor))
object Mul {
implicit def commutative[A, Tag1, Tag2, Tag3](implicit mul: Mul[A, Tag1, Tag2, Tag3]) =
Mul[A, Tag2, Tag1, Tag3](mul.factor)
}
}
$ scala -version
Scala code runner version 2.11.8 -- Copyright 2002-2016, LAMP/EPFL
case class UnitBase[A, Tag](value: A) extends AnyVal
def calcCpm(imp: Imp, yen: Yen): Cpm =
Cpm(yen.value.toDouble / imp.value.toDouble * 1000)
trait CpmTag
type Cpm = UnitBase[Double, CpmTag]
object Cpm {
def apply(value: Double) = new Cpm(value)
}
trait Numeric[T] extends Ordering[T] {
def plus(x: T, y: T): T
def minus(x: T, y: T): T
def times(x: T, y: T): T
trait Integral[T] extends Numeric[T] {
trait IntIsIntegral extends Integral[Int] {
def plus(x: Int, y: Int): Int = x + y
def minus(x: Int, y: Int): Int = x - y
def times(x: Int, y: Int): Int = x * y
implicit object IntIsIntegral extends IntIsIntegral with Ordering.IntOrdering
case class UnitNum[A, Tag](value: A) extends AnyVal {
def +(x: UnitNum[A, Tag])(implicit op: Numeric[A]) =
UnitNum[A, Tag](op.plus(value, x.value))
def -(x: UnitNum[A, Tag])(implicit op: Numeric[A]) =
UnitNum[A, Tag](op.minus(value, x.value))
def *[Tag1, Tag2](x: UnitNum[A, Tag1])(implicit op: Numeric[A], mul: UnitNum.Mul[A, Tag, Tag1, Tag2]) =
UnitNum[A, Tag2](op.times(op.times(value, x.value), mul.factor))
def /[Tag1, Tag2](x: UnitNum[A, Tag1])(implicit op: Fractional[A], mul: UnitNum.Mul[A, Tag1, Tag2, Tag]) =
UnitNum[A, Tag2](op.div(op.div(value, x.value), mul.factor))
def map[B](f: A => B) =
UnitNum[B, Tag](f(value))
def unary_-(implicit op: Numeric[A]) =
map(op.negate)
def abs(implicit op: Numeric[A]) =
map(op.abs)
def mapInt(implicit op: Numeric[A]) =
map(op.toInt)
def mapLong(implicit op: Numeric[A]) =
map(op.toLong)
def mapFloat(implicit op: Numeric[A]) =
map(op.toFloat)
def mapDouble(implicit op: Numeric[A]) =
map(op.toDouble)
}
object UnitNum {
import scala.language.implicitConversions
implicit def ordering[A: Ordering, Tag]: Ordering[UnitNum[A, Tag]] =
Ordering.by(_.value)
implicit def ordered[A: Ordering, Tag](x: UnitNum[A, Tag]): Ordered[UnitNum[A, Tag]] =
Ordered.orderingToOrdered(x)(ordering[A, Tag])
case class Mul[A, Tag1, Tag2, Tag3](factor: A) extends AnyVal
object Mul {
implicit def commutative[A, Tag1, Tag2, Tag3](implicit mul: Mul[A, Tag1, Tag2, Tag3]) =
Mul[A, Tag2, Tag1, Tag3](mul.factor)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment