Last active
June 16, 2017 12:14
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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