-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
coulomb-runtime for scala3 #420
Conversation
0878136
to
618fbf3
Compare
I'm reframing this as |
It's working. scala> import scala.quoted.*, coulomb.runtime.*, coulomb.runtime.syntax.*, coulomb.*, coulomb.syntax.*, coulomb.rational.Rational, coulomb.policy.standard.given
scala> given staging.Compiler = staging.Compiler.make(classOf[staging.Compiler].getClassLoader)
lazy val given_Compiler: quoted.staging.Compiler
scala> 1d.withUnitRuntime[coulomb.units.us.Yard](UnitAST.of[coulomb.units.si.Meter])
val res0: coulomb.Quantity[Double, coulomb.units.us.Yard] = 1.0936132983377078 |
@armanbilge My initial purpose here was to support the scala-2 "parsing" on scala-3, which this does, but I'm also starting to think it might allow me to support your earlier request for "runtime Quantities" without creating an entire new parallel universe of runtime defs. You can see that my pair There would be some work to do on what the most elegant design is, but this now makes it seem feasible. scala> import scala.quoted.*, coulomb.runtime.*, coulomb.runtime.syntax.*, coulomb.*, coulomb.syntax.*, coulomb.rational.Rational, coulomb.policy.standard.given, coulomb.units.si.{*, given}, coulomb.units.time.{*, given}, coulomb.units.us.{*, given}
scala> given staging.Compiler = staging.Compiler.make(classOf[staging.Compiler].getClassLoader)
lazy val given_Compiler: quoted.staging.Compiler
scala> val rq = (1d, RuntimeUnit.of[Meter / Second])
val rq: (Double, coulomb.runtime.RuntimeUnit) = (1.0,Div(UnitType(coulomb.units.si$.Meter),UnitType(coulomb.units.si$.Second)))
scala> rq.toQuantity[Yard / Minute]
val res0:
Either[String,
coulomb.Quantity[Double, coulomb.units.us.Yard / coulomb.units.time.Minute]
] = Right(65.61679790026247) |
f145b96
to
293cf75
Compare
So in a hypothetical world with |
This is a very interesting idea! I think you are right—it lets you bridge that gap. The only catch is I'm guessing that runtime staging is not support on Scala.js or Scala Native. But it's worth experimenting with on the JVM. |
@armanbilge it looks like you are right - I added a unit test, which runs in JVM but doesn't in either JS or Native. That's a damn shame, because I'm really starting to like how it is shaping up. Tangent: is there a corresponding |
@armanbilge I might experiment with other approaches. One thing that is nice about this is that it appears to work without me explicitly generating any maps from types to corresponding static unit defs. The calls to But I could go back to experimenting with constructing such maps, which might allow similar resolutions at runtime. It would be slightly annoying because I'd need to duplicate the logic of |
This is so clean, I feel unsatisfied with anything else 😂 |
Yep, see: sbt/sbt-header#282. You might need to add the latest version of
Yeah honestly this is one of the best applications I've seen of staging. It maybe even challenges the premise from that post I think I shared a while back, about the inevitability of defining things in both the runtime and compile time layer. So I think this is worth thinking more about, and talking about :) Scala is not there yet, but maybe it will eventually be able to do something like this on all platforms. |
@armanbilge at least for coulomb, I feel like this unambiguously shows we can support both runtime and compile-time with the existing static unit defs. I have no idea how general that result is for other applications. I'm still a bit confused about how it's working here, but it definitely is. As far as support for JS and Native, in theory it seems like it could. It knows at compile-time what the result-type of the runtime expression will be, so the staging compiler could run against the AST and leave behind the result in whatever JS or Native is expecting. I don't have much feel for whether the quoted expressions are expressible in JS or native, or if they need to be. |
@armanbilge I think the thing I'm most curious about is that |
@armanbilge I think I have a solution that feels good. I defined a new concept called |
@armanbilge this should work on JS and Native: Welcome to Scala 3.2.2 (17.0.5, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
scala> import scala.quoted.*, coulomb.runtime.*, coulomb.runtime.syntax.*, coulomb.*, coulomb.syntax.*, coulomb.rational.Rational, coulomb.policy.standard.given, coulomb.units.si.{*, given}, coulomb.units.time.{*, given}, coulomb.units.us.{*, given}, coulomb.units.si.prefixes.{*, given}
scala> import coulomb.runtime.conversion.runtimes.mapping.*
scala> import MappingCoefficientRuntime.{TNil, &:}
scala> MappingCoefficientRuntime.of[Kilogram &: Meter &: Second &: Kilo &: Yard &: TNil]
val res0: coulomb.runtime.conversion.runtimes.mapping.MappingCoefficientRuntime = coulomb.runtime.conversion.runtimes.mapping.MappingCoefficientRuntime@78696450
scala> res0.coefficientRational(RuntimeUnit.of[Kilo * Meter], RuntimeUnit.of[Meter])
val res1: Either[String, coulomb.rational.Rational] = Right(1/1000)
scala> res0.coefficientRational(RuntimeUnit.of[Kilo * Meter], RuntimeUnit.of[Second])
val res2: Either[String, coulomb.rational.Rational] = Left(non-convertible units: Kilo*Meter, Second)
scala> |
As a convenience, I now support module names, so it will automatically ingest all defined units in a given module: MappingCoefficientRuntime.of["coulomb.units.si" &: "coulomb.units.si.prefixes" &: TNil] |
|
d33f633
to
475db4e
Compare
From here on out, I think I'm going to want to co-implement this with |
It would be nice to support polymorphic inputs with |
3816859
to
b1fd7d2
Compare
expression parsing for |
91e56d3
to
f69aca6
Compare
8dfd15e
to
49e8e4b
Compare
8129900
to
018c1ea
Compare
This PR is to develop unit handling at runtime for scala3 coulomb, using the new staging compiler and phased metaprogramming.
https://docs.scala-lang.org/scala3/reference/metaprogramming/staging.html
breadcrumb xref for running staging compiler in sbt repl: scala/scala3#7647