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

Make RefinedTypeOps definition more ergonomic #253

Open
Iltotore opened this issue Jul 30, 2024 · 2 comments
Open

Make RefinedTypeOps definition more ergonomic #253

Iltotore opened this issue Jul 30, 2024 · 2 comments
Assignees
Labels
breaking changes Changes that break compatibility with older versions enhancement New feature or request
Milestone

Comments

@Iltotore
Copy link
Owner

Currently, declaring a new type requires to declare the constraint in the opaque type and in RefinedTypeOps's second argument which can be problematic for long constraints. Example derived from Valentin Bergeron and Raphaël Lemaitre's talk

opaque type Sats = Long :| GreaterEqual[0] & LessEqual[100000000 * 21000000]
object Sats extends RefinedTypeOps[Long, GreaterEqual[0] & LessEqual[100000000 * 21000000], Sats]

An alias can be used to mitigate the boilerplate:

private type SatsConstraint =
  GreaterEqual[0] & LessEqual[100000000 * 21000000]

opaque type Sats <: Long = Long :| SatsConstraint
object Sats extends RefinedTypeOps[Long, SatsConstraint, Sats]

Ideally, the redundancy should be removed either by passing only the third parameter in RefinedTypeOps:

opaque type Sats = Long :| GreaterEqual[0] & LessEqual[100000000 * 21000000]
object Sats extends RefinedTypeOps[Sats]

or defining the type alias from the companion object (like Neotype):

opaque type Sats = Sats.Type
object Sats extends RefinedTypeOps[Long, GreaterEqual[0] & LessEqual[100000000 * 21000000]]

I think defining the ops from the opaque type (first solution) is more natural. It was not possible before but might be doable easily enough since TypeRepr#dealias in Scala 3.4+ also dealiases opaque types. Thus, a mirror-like mechanism can (need to be tested) be used internally:

trait RefinedTypeOps[T](using mirror: IronType.Mirror, constraint: RuntimeConstraint[mirror.BaseType, mirror.ConstraintType])

On the other hand, solution 2 is pretty easy to do:

trait RefinedTypeOps[A, C]:

  type Type = A :| C

Users' opinions need to be collected in order to make a choice.

@Iltotore Iltotore added enhancement New feature or request breaking changes Changes that break compatibility with older versions labels Jul 30, 2024
@Iltotore Iltotore added this to the 3.0.0 milestone Jul 30, 2024
@vbergeron-ledger
Copy link
Contributor

I 👍 the solution from the opaque type definition is cleaner : more readable and the companion object fulfill more its role of, well, companion.

@Iltotore
Copy link
Owner Author

Iltotore commented Sep 1, 2024

Unfortunately the second solution does not seem doable at the moment. Scala is missing inline constructor parameters.

Since this feature will probably not land until long time, maybe it's worth to implement the first proposal instead?

What do you think @vbergeron-ledger?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
breaking changes Changes that break compatibility with older versions enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants