项目作者: rudogma

项目描述 :
Scala: Typelevel unboxed compile time dimensional analysis over tagged types. Intellij Idea compatible 100%
高级语言: Scala
项目地址: git://github.com/rudogma/scala-superquants.git
创建时间: 2017-06-23T16:04:54Z
项目社区:https://github.com/rudogma/scala-superquants

开源协议:MIT License

下载


Build status
Maven Central

Typelevel unboxed compile time dimensional analysis over tagged types.

sbt

Scala: 2.11.11, 2.12.1, 2.12.2

  1. libraryDependencies += "org.rudogma" %% "superquants" % "0.9"

ScalaJS (compiled with 0.6.17)

  1. libraryDependencies += "org.rudogma" %%% "superquants" % "0.9"

Features

  • Intellij Idea compatible 100%. Red marks free.
  • Typelevel (no boxing for primitives)
  • Compile-time
  • Using tagged types (library: https://github.com/Rudogma/scala-supertagged)
  • Supported physical rules (examples below)
  • Auto-plaining after working with complex types (example below)
  • Long & Double precision included
  • Extended prettify ops
  • Support for aliasing complex types. (Like Acceleration or any other)
  • Predefined aliases for power (Single, Squared, Cubic & their negative variants)
  • Predefined Units
    • Length (7 units)
    • Time (8 units)
    • Mass (17 units)
    • Binary (19 units)
  • Performance
    • There is no any additional garbage for GC! All implicit evidences are reuse singleton objects.
    • There is no boxing, so they are much faster then AnyVal, but there is some boilerplate-bytecode, so they are slower then raw primitives without tags. This boilerplate can be removed with micro-scalac-plugin and then there will be no difference between this library and raw primitives.
    • Long precision ( ~10x slower on big formulas, than primitive Long)(Slower then Double precision, because additional convertions Double -> Long applied)
    • Double precision( ~2-4x slower on big formulas, than primitive Double)( But! It is 7-10x FASTER then based on AnyVal: https://github.com/typelevel/squants and still has no garbage for GC)
  • Define your own unit(or extend predefined) in a few lines and automatically get supported all physical rules (see predefined units in sources. For example: Length1 + Length2

Roadmap

This is a snapshot of pre-release. Release planned at July’2017
For now almost all physical rules supported for plain, powered and complex(fractional) units. However there some cases that i didn’t test yet. I will check them all in a few weeks. Also I plan to add some more features before release to power up usability.

Usage

Check out tests for more examples

Basic terms

I think you already know what is Tagged types and how they work ( or U can read some more at: https://github.com/Rudogma/scala-supertagged ). In supertagged I’ve made new level of tagging and called this OverTagged. TaggedType and OverTagged - are used upon this library heavily.

Define some val(-r)s

  1. import superquants._
  2. import longprecision._
  3. import longprecision.length._
  4. import longprecision.time._
  5. import shapeless.{ ::, HNil, Nat }
  6. val meters:Meters = 5.meters
  7. val seconds:Seconds = Seconds @@ (Time @@ 5L)
  8. val squaredSeconds:Pow[Long, Seconds, PowPlus, Nat._1] = 5L.as[Pow[Long, Seconds, PowPlus, Nat._1]]
  9. // or using alias
  10. val squaredSeconds2:Squared[Seconds] = 5L.as[Squared[Seconds]]
  11. val speed:Complex[Long, Pow[Long, Meters, PowPlus, Nat._1] :: Pow[Long,Seconds,PowMinus, Nat._1]] = 5L.as //as[T] - will auto use explicit type from the left
  12. //aliased
  13. val speed2:Complex[Long, Single[Meters] :: Negative.Single[Seconds] :: HNil] = 5L.as
  14. type Speed = Complex[Long, Single[Meters] :: Negative.Single[Seconds] :: HNil]
  15. val speed22:Speed = 5L.as
  16. /**
  17. * Library based on a simple physical rules. So...
  18. */
  19. val metersXseconds = 5.meters ** 5.seconds
  20. val speed3:Speed = 5.meters divide (5.seconds ** 5.seconds)
  21. val plainMeters:Meters = speed3 ** (5.seconds ** 5.seconds)
  22. // annihilate units
  23. val plainLong = speed3 ** (5.seconds ** 5.seconds divide 5.meters)
  24. val meters2:Meters = 5.meters ++ 5.meters
  25. //but, U can't do it with primitive
  26. // meters2 = 5.meters ++ 5L // Wil not compile!!! Physicists denied that!
  27. //but, U can multiply unit with raw primitives
  28. val meters3:Meters = 5.meters ** 5L

Limitations

The cost of performance:
As U see above I’ve used ‘**’ for multipying. Because our types are tagged on type level(and so unboxed) and have no materialized mixins at runtime we can’t overload primitive operators.
Supported operators:

  • --
  • ++
  • ``**
  • divide //for /, because // - starts comment :)
Note!

Because our types are primitives. Primitive operators also works and provide raw primitive value

  1. val meters:Meters = 5.meters * 5.seconds // fails, because we got raw Long. But compiler sees Meters at the left and will safe us.

Physicists agree. If we multiply meters on seconds we will get just number. Not seconds, not meters, not other complex unit. Just Number. These remains all primitive operators.

Aliasing

  1. import superquants._
  2. import longprecision._
  3. import longprecision.length._
  4. import longprecision.time._
  5. import shapeless.{::, HNil}
  6. //Using aliases in longprecision.package.scala
  7. type Acceleration = Complex[Long, Single[Meters] :: Squared[Seconds] :: HNil]
  8. //Equivalent in a more verbose: type Acceleration = Complex[Long, Pow[Long, Meters, PowPlus, Nat._1] :: Pow[Long, Seconds, PowMinus, Nat._2] :: HNil]
  9. val acc = 5.meters divide (5.seconds ** 5.seconds)
  10. //Or we can explicitly specify alias if we think we could forget something. Let compiler do some work!
  11. val acc2:Acceleration = acc
  12. // will accept only accelerations
  13. def onlyAcceleration(acc:Acceleration):Unit = {}

Convert

  1. import superquants._
  2. import longprecision._
  3. val mg:Milligrams = 5.kg in Grams // Got: 5000 Milligrams

Prettify

  1. import superquants._
  2. import render._
  3. import longprecision.mass._
  4. 10.kg.pretty shouldEqual "10 kg"
  5. //Or U can specify how much details u need
  6. 11123456789L.mg.pretty[Tonnes :: Kilograms :: Grams :: Milligrams :: HNil] shouldEqual "11 t 123 kg 456 g 789 mg"
  7. 11123456789L.mg.pretty[Tonnes :: Milligrams :: HNil] shouldEqual "11 t 123456789 mg"