Skip to content
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

refined integration for scala3 #392

Merged
merged 36 commits into from
Jan 7, 2023
Merged

refined integration for scala3 #392

merged 36 commits into from
Jan 7, 2023

Conversation

erikerlandson
Copy link
Owner

This initial concept only addresses refined predicates that satisfy algebras such as additive semigroup.

@erikerlandson
Copy link
Owner Author

xref: #385

Comment on lines 31 to 32
def plus(x: Refined[V, Positive], y: Refined[V, Positive]): Refined[V, Positive] =
refineV[Positive].unsafeFrom(alg.plus(x.value, y.value))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if this addition overflows?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be an error, but I haven't worked out a way around it. Technically I think it's type unsound, since if it did overflow it could result in a state not reflected by the type.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I can check with the validator "manually"

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only truly type sound semantic would be to have the resulting value be the Either expression, which seems awkward to me, however it would work.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it "works" in the sense that any type can be a Quantity value, but the algebra typeclass requires the original V type.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At any rate, the current behavior is NOT unsound

But note that it is unlawful.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also probably depends on whether you are semantically trying to represent a modular ring (as in that example) or an unbounded additive semigroup.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the bright side, AdditiveSemigroup for + will admit either my algebra definitions or a user's preferred definition, whichever they want to import.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, one of my design philosophies has been that the "out-of-box" definitions provided by coulomb deliberately do not try to obscure the "native" behaviors of numeric types. So integer addition can overflow. Floating points do whatever complicated native floating point behaviors, etc.

Users may very reasonably wish to define better semantics, but the definitions of "better" have a lot of potential variation, and I decided it was clarifying to me to avoid that kind of potential for policy explosion.

Copy link
Contributor

@armanbilge armanbilge Nov 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. FWIW, the design philosophy of algebra AdditiveSemigroup and friends is that implementing instances are lawful. The laws are the semantics.

@erikerlandson
Copy link
Owner Author

the "algebraic" policy overlays seem to work:

scala> import eu.timepit.refined.*, eu.timepit.refined.api.*, eu.timepit.refined.numeric.*, coulomb.policy.standard.{*, given}, algebra.ring.*, algebra.instances.all.given, coulomb.units.si.{*, given}, coulomb.units.si.prefixes.{*, given}, coulomb.*, coulomb.syntax.*, coulomb.policy.overlay.refined.algebraic.given
                                                                                                                                
scala> refineV[Positive].unsafeFrom(1d).withUnit[Meter]
val res0: 
  coulomb.Quantity[Double Refined eu.timepit.refined.numeric.Positive, 
    coulomb.units.si.Meter
  ] = 1.0
                                                                                                                                
scala> refineV[Positive].unsafeFrom(1).withUnit[Kilo * Meter]
val res1: 
  coulomb.Quantity[Int Refined eu.timepit.refined.numeric.Positive, 
    coulomb.units.si.prefixes.Kilo
   * coulomb.units.si.Meter] = 1
                                                                                                                                
scala> res0 + res1
val res2: 
  coulomb.Quantity[Double Refined 
    eu.timepit.refined.numeric.Greater[shapeless.nat._0]
  , coulomb.units.si.Meter] = 1001.0

@benhutchison
Copy link

Looks very promising. Just yesterday was experimenting with porting some code onto Coulomb, where I divided 24 hours by a short time interval duration, to calculate how many such intervals occur per day. And it made me think that once this refinement is available (for division), the result could be inferred as positive. Currently, I re-assert the result is positive since I don't have inference..

@erikerlandson
Copy link
Owner Author

erikerlandson commented Nov 27, 2022

xref fthomas/refined#932

@benhutchison
Copy link

@erikerlandson If you're willing to cut a beta or RC binary release off this branch sometime, I'd be keen to experiment with it in an application codebase and provide feedback.

@benhutchison
Copy link

I'm a little unsure if you're dependent on changes in Refined to make this viable or if you consider it near usable in present form.

@erikerlandson
Copy link
Owner Author

@erikerlandson
Copy link
Owner Author

@benhutchison regarding fthomas/refined#932, I am not blocked on that. It basically means (for now) I will be providing refineVU[P, U] but NOT refineMVU[P, U] (the compile-time checking version)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants