Skip to content

Instantly share code, notes, and snippets.

@rudogma
Last active June 16, 2017 12:14
Show Gist options
  • Save rudogma/03eb32b9bbe98243713d138ee825c2b8 to your computer and use it in GitHub Desktop.
Save rudogma/03eb32b9bbe98243713d138ee825c2b8 to your computer and use it in GitHub Desktop.
Using tagged types as alternative (with no runtime overhead) to Compile time dimensional analysis at http://typelevel.org/blog/2017/06/13/libra.html
import supertagged._
/*** ONE TIME PREDEF **/
trait Opp {
type Operation
type Divide <: Operation
type Multiply <: Operation
}
object Opp extends Opp
import Opp._
@implicitNotFound("Not found Law to make Operation[${Op}] for operand1: ${P1}, and operand2: ${P2}")
trait Law[P1, Op <: Operation, P2] {
type Out
def apply(p1:P1, p2: P2):Out
}
type AuxLaw[P1, O <: Operation, P2, Out0] = Law[P1, O, P2] { type Out = Out0 }
private def dummy[P1, O <: Operation, P2, Out0]( f: (P1,P2) => Any) = new Law[P1, O, P2] {
type Out = Out0
def apply(p1: P1, p2: P2): Out0 = f(p1,p2).asInstanceOf[Out0]
}
implicit class LawOps[U, T](val op1:T @@ U) extends AnyVal {
def divide[T2](op2:T2)(implicit law:Law[T @@ U, Divide, T2], d:DummyImplicit):law.Out = law(op1, op2)
def divide[T2,U2](op2:T2 @@ U2)(implicit law:Law[T @@ U, Divide, T2 @@ U2]):law.Out = law(op1, op2)
def **[T2](op2:T2)(implicit law:Law[T @@ U, Multiply, T2], d:DummyImplicit):law.Out = law(op1, op2)
def **[T2,U2](op2:T2 @@ U2)(implicit law:Law[T @@ U, Multiply, T2 @@ U2]):law.Out = law(op1, op2)
}
/** DEFINE TYPES **/
object Counter extends TaggedType[Int]
type Counter = Counter.Type
object Kilogram extends TaggedType[Double]
type Kilogram = Kilogram.Type
object Second extends TaggedType[Int]
type Second = Second.Type
type Seconds = Second.Type
object MetresPerSecond extends TaggedType[Double]
type MetresPerSecond = MetresPerSecond.Type
object KilogramPerSecond extends TaggedType[Double] {
implicit class Ops(val __v:Type) extends AnyVal {
def pretty: String = s"${__v} kg/s"
}
}
type KilogramPerSecond = KilogramPerSecond.Type
object Metre extends TaggedType[Int]
type Metre = Metre.Type
object SquareMetre extends TaggedType[Int]
type SquareMetre = SquareMetre.Type
/** DEFINE postfix convertions **/
implicit class DoubleOps(val __v:Double) extends AnyVal {
def kg:Kilogram = Kilogram @@ __v
}
implicit class IntOps(val __v:Int) extends AnyVal {
def kg:Kilogram = Kilogram @@ __v.toDouble
def seconds:Second = Second @@ __v
def metre:Metre = Metre @@ __v
}
/*** DEFINE LAWS for our TYPES***/
implicit val law0:AuxLaw[Kilogram, Divide, Second, KilogramPerSecond] = dummy( (p1,p2) => p1 / p2)
implicit val law1:AuxLaw[Kilogram, Divide, Int, Kilogram] = dummy( (p1,p2) => p1 / p2)
implicit val law3:AuxLaw[Metre, Multiply, Int, Metre] = dummy( (p1,p2) => p1 * p2)
implicit val law4:AuxLaw[Metre, Multiply, Metre, SquareMetre] = dummy( (p1,p2) => p1 * p2)
/** OUR PROGRAM **/
val kgValue = 100.0.kg
val sValue = 5.seconds
val cc = Counter @@ 555
val result = kgValue divide sValue // using law0, KilogramPerSecond
val result1 = kgValue divide 55 // using law1, Kilogram
// val result2 = kgValue divide cc // Not found Law to make Operation[...Divide] for operand1: Double @@ Kilogram.Tag, and operand2: Int @@ Counter.Tag
println("result: "+result.pretty) //result: 20.0 kg/s
val m1 = 100.metre ** 100 // using law3, Metre
// val m2:SquareMetre = m1 * m1 // fails, if we forgot using '**'
val m2 = m1 ** m1 //SquareMetre
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment