Giter Club home page Giter Club logo

coulomb's Introduction

coulomb's People

Contributors

armanbilge avatar erikerlandson avatar scala-steward avatar scala-steward-erikerlandson[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

coulomb's Issues

Switch to munit

What do you think about switching to munit for tests. The main benefit is that we can get dotty support sooner.

If you're ok with that I could prepare a PR

Make implicit hints possible

Currently, one is unable to provide a MulResultType because it is package private. IntelliJ has a lot of problems resolving the implicits so type inference is very poor. This could be improved by providing some of these instances explicitly.

Support for types like `Int with U <: UnitExpr`

This might be a very powerful feature, although it comes with some drawbacks and problems to solve:

  1. the with U part isn't closed under various numeric operations
  2. easy to drop the with U part by assigning to non-qualified numeric values
  3. have to figure out rules and support for operations with differing types
  4. e.g. what is result type of Int with U <+> Float with U
  5. Numeric[N] doesn't seem to support construction of new N from arbitrary other values

mdoc:fail behaving weirdly

@armanbilge I am seeing weird behaviors for mdoc code blocks where error (:fail) is expected.

It is behaving as if it is keeping a previous error message instead of reporting the actual error message:

image

That error is from previous fail demo with a deliberately bad Quantity expression, instead of the error for 7 + false

Support "volatile" units whose coefficients may change over time or context

The main use case I have in mind so far is currency units, since their values relative to each other fluctuate over time.

A few possible concepts. 1) a new companion subclass VolatileUnitCompanion, whose coef value could be set. Macros would (somehow) detect these and pull this coef variable into the expression. Note that this makes the coefficient a global variable, having all the problems that come along with global variables.

  1. Ability to construct conversion functions with parameters: converterWithVolatileCoef You send in a variable-arg list of C <: UnitCompanion[_] (can this work with the type erasure?). The conversion coefficient expression pulls in function paramters in the appropriate places. This is nice because it's a referentially transparent solution. Also it could be applied to any unit (no special volatile subclass). But might propagate through a lot of code if it's working with volatile units.

  2. Some scoping utility for implicits that override. Not sure how this would work, or if it could be accomplished at runtime in a meaningful way.

timestamp as an analog to temperature

For temperature and quantities of degrees, we have:

Temp +/- Quantity => Temp
Temp - Temp => Quantity

A very similar relationship applies to Quantity of time units and date-time stamps.

sequences / collections with units

CoulombVector, CoulombList, etc. It is straightforward to declare Vector[Quantity[...]] but more efficient to abstract out the unit types

scala 3 roadmap

This will be my tracking issue for figuring out how to approach scala 3

branching

coulomb for scala 3 will start as a PR, but once it merges I'm going to make it the main (develop) branch going forward, and whatever the most recent version is with scala 2, that will become a maintenance-only branch.

scala 2 compatibility mode

I may try building with scala-2/3 compatibility mode as an interim - that is, making no code changes but compiling for scala 3 as part of the crossbuild. If crossbuild works well enough, this phase can be part of existing release series, with no maintenance branching needed.

implicit -> summon

The main syntactic changes will be around contextual polymorphism, for example the new summon keyword. I expect this to be a fairly mechanical change to the existing code but it will be very pervasive. Once this change takes place, previous release will be its own maintenance branch.

QuantityParser

I'll need to figure out if the existing implementation of QuantityParser will survive the transition to scala 3. See also #99.

Scala 3 metaprogramming

Scala 2 macro paradise was hard to work with and somewhat deprecated. Scala 3 metaprogramming appears to be a big improvement and is a first-class language feature. One thing to do is look for opportunities to re-incorporate metaprogramming if that makes sense.

other scala 3 features?

If there are any other scala 3 language features coulomb can leverage, they can be incorporated after the large-scale implicit -> summon changes.

quantities of any numeric type

Related to #2, but would apply to Quantity, making its internal value type non-opaque.

The tricky part of this is rules for interaction between values of differing types, and output types as function of input types. Also touches on internal representation of conversion factors, and understanding if and when to preserve "integer-ness"

re-design to pick up implicits outside of macros to the extent possible

I pushed a lot of implicit search down into macros. Some of the reasons for that never actually happened, and this makes it harder to integrate with other libraries. Investigate backing out of this and going to directly declared implicits again.

Related - to preserve some of the nice efficiencies, look into implicit manifestations having in-line methods

Allow any type, including predefined ones, to become a base unit

The coefficient of a base unit is always 1. The only thing I need for a base unit is (1) a way to detect that it is a base unit, and (2) long and abbreviated name.

Allowing any type to serve as a base unit might be very powerful - associating quantities with any kind of object.

Directly declaring an implicit UnitDecl is one possible option, or using a special variation to detect when something is declared as a BaseUnit directly, or if it is being applied to an existing type

evaluate coulomb 0.5.0-RC1

This is an issue to track smoke-testing publication of 0.5.0-RC1, which is the first release cross-published to scala.js and jvm.

Document basic numeric and unit conversion principals

There are a few important principals to document that will help users of this library to reason about its numeric behavior.

  1. units and value types are "left-oriented" - a right-hand quantity is always converted to the units and value type of the left-hand quantity.
  2. unit conversions are performed first, and then the actual numeric ops happen using the left-hand value type
  3. conversion from one value type to another is "best effort", but information is ultimately lost when doing conversions such as "double" to "int"
  4. The usual limitations of all numeric types apply. For example, the loss of precision when subtracting floating point numbers that are nearly equal. Numeric operations involving integer value types involve all the usual quantizations. 5 / 2 => 2.

example to use involving integer value types:

(100.withUnit[Meter]) + (1.withUnit[Kilo %* Meter])  // evaluates to 1100 meters
(1.withUnit[Kilo %* Meter]) + (100.withUnit[Meter])  // evaluates to 1 kilometer

Use more specialized type classes instead of Numeric

Currently, all classes in the unitops package have a hard dependency on Numeric[N] whilst this could be more specialized.

An example of what such a specialized operation would look like:

trait UnitMultiply[N1, U1, N2, U2] {
  def times(x: N1, y: N2): N1
  type RT12
}
object UnitMultiply {
  type Aux[N1, U1, N2, U2, R12] = UnitMultiply[N1, U1, N2, U2] {
    type RT12 = R12
  }
  implicit def evidence[N1, U1, N2 : ConvertableFrom, U2](implicit
                                        ms: MultiplicativeSemigroup[N1],
                                        ct: ConvertableTo[N1],
                                        mrt12: MulResultType[U1, U2]): Aux[N1, U1, N2, U2, mrt12.Out] =
    new UnitMultiply[N1, U1, N2, U2] {
      def times(x: N1, y: N2): N1 = ms.times(x, ct.fromType[N2](y))
      type RT12 = mrt12.Out
    }
}

Scalar operations

Should it be allowed to do scalar operations like multiply 2 meters by 4?
I guess addition and substraction are not possible with scalars but multiplication and division should be possible

Remove dependency on spire

The approach would be to port spire's Rational into the coulomb tree. The typeclasses for AdditiveGroup and friends can be obtained from typelevel/algebra. The spire package could then become %Test

comparison ops

%<, %>, %==, etc.

Should probably define Ordered type class for these if possible.

make implicit unit/numeric conversions opt-in?

Currently, coulomb will implicitly make numeric and/or unit conversions whenever it is possible.

Under some philosophies, that isn't desirable. It is possible to separate out these implicit rules,
so they can be either imported or not, as desired. If not, then one would have to explicitly
invoke .toUnit and toNumeric to convert, or compilation would fail.

generalize operator result value types away from left-centric

Currently, the output value type of binary operations is taken to be the type of the LHS:

scala> 1f.withUnit[Foot] + 1.withUnit[Yard]
val res0: coulomb.Quantity[Float,coulomb.us.Foot] = Quantity(4.0)

The output's value type is the lhs Float, not the rhs Int.

This is a sane policy, but it might be improved on. For one example, making the output type a true function of lhs X rhs would allow the more traditional numeric behaviors like (int + float) -> float, etc.

Another tantalizing example would be applications to the Refined integrations. Currently the design constraint of LHS binding requires me to be concerned with issues of closure: the LHS refined predicate must be closed under operations. Again, this is sane, but if I could make the output Refined predicate a function of LHS and RHS predicates, it would open up the possibility of things like:

1.withRefinedUnit[GreaterEqual[3], Meter] + 1.withRefinedUnit[GreaterEqual[4], Meter]
===>
type: Quantity[Refined[Int, GreaterEqual[7]], Meter]

coming up with a sane policy for combining this with the effects of unit conversions would be challenging, but it would be powerful if I could pull it off

Should the unit type `U` in Quantity[N, U] be covariant?

trait Fruit
trait Apple extends Fruit
trait Pear extends Fruit

val q1 = 1.withUnit[Apple] + 1.withUnit[Pear]  // is q1 Quantity[Int, Fruit] ?

def f1(f: Quantity[Int, Fruit]) = { ... }

f1(1.withUnit[Apple])
f1(1.withUnit[Pear])

would likely depend on #22

Scala 3 tests are not running on JS or Native

This is because of the %% instead of %%%.

libraryDependencies += "org.scalameta" %% "munit" % "0.7.29" % Test

Actually, at the moment it's impossible to run the tests at all on Native, since the munit support is blocked:

We have a couple options:

  1. Keep the status quo for now, since the %% effectively disables the platform tests anyway (maybe this was intentional? :)
  2. Temporarily drop Native, so we can at least enable tests on JS which is an important platform for the folks at @gemini-hlsw.

Support a `V %% U` synonym for `Quantity[V, U]`

I'd consider even making V %% U the primary type and Quantity the synonym for backward compatability.

Related - use %%% for OffsetQuantity? Are there good synonyms for Temperature and EpochTime?

cats instances

It would be interesting to define cats instances for quantity like Eq, Order, and more

value types that are functors, and bifunctor operations

the integration with Refined turned up an interesting pattern. the implementations of operations looked like this:

    new UnitAdd[Refined[V1, P1], U1, Refined[V2, P2], U2] {
      def vadd(v1: Refined[V1, P1], v2: Refined[V2, P2]): Refined[V1, P1] = {
        add.vadd(v1.value, v2.value).applyPred[P1]
      }

The pattern was un-packing lhs and rhs payloads, operating on them, and re-packing them. This basic logic generalizes to operands that are functors, particularly Option, Either, etc. But possibly even other nontrivial functors like Seq

It also suggests that these operations themselves are bifunctors. Can they be implemented using cats Bifunctor and bimap ?

scala3 questions

I did a first attempt trying to use coulomb in scala 3. I got quite a bit of progress on a relatively simple library that uses coulomb but I have some errors I can't solve, having tried several imports.

How could I get this work?

[error] -- Error: /Users/carlos.quiroz/code/noirlab/lucuma-itc/modules/itc/src/main/scala/lucuma/itc/ItcImpl.scala:150:40 
[error] 150 |                  ((expTime - oldExpTime) > 1.withUnit[Second] || (oldExpTime - expTime) > 1
[error]     |                                        ^
[error]     |Subtraction not defined in scope for Quantity[BigDecimal, coulomb.units.si.Second] and Quantity[BigDecimal, coulomb.units.si.Second]
[error]     |
[error]     |One of the following imports might make progress towards fixing the problem:
[error]     |
[error]     |  import coulomb.policy.spire.standard.ctx_sub_1V1U
[error]     |  import coulomb.policy.spire.standard.ctx_sub_1V2U
[error]     |  import coulomb.policy.spire.standard.ctx_sub_2V1U
[error]     |  import coulomb.policy.spire.standard.ctx_sub_2V2U
[error]     |  import coulomb.policy.spire.strict.ctx_sub_1V1U
[error]     |  import coulomb.policy.spire.strict.ctx_sub_1V2U
[error]     |  import coulomb.policy.spire.strict.ctx_sub_2V1U
[error]     |  import coulomb.policy.spire.strict.ctx_sub_2V2U
[error]     |  import coulomb.policy.standard.ctx_sub_1V1U
[error]     |  import coulomb.policy.standard.ctx_sub_1V2U

I'm importing

import coulomb.policy.spire.standard.`given`
import coulomb.ops.algebra.spire.all.*

I have a few similar errors for division and multipaction. I guess I'm missing an algebra?

Any guidance would be appreciated

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.