Skip to content
This repository has been archived by the owner on Jun 5, 2023. It is now read-only.

Opaque types and inline defs limitation #82

Closed
soronpo opened this issue Dec 7, 2019 · 12 comments
Closed

Opaque types and inline defs limitation #82

soronpo opened this issue Dec 7, 2019 · 12 comments

Comments

@soronpo
Copy link

soronpo commented Dec 7, 2019

Tried to use opaque types to create a compile time constrained Positive integer type:

import compiletime._

opaque type Positive = Int
object Positive {
  inline def apply(inline value : Int) : Positive = {
    inline if (value <= 0) error(code"failed on: $value")
    else value
  }
}

val eight = Positive(8)
val negError = Positive(-1) //expect error here

Unfortunately I got the error Implementation restriction: No inline methods allowed where opaque type aliases are in scope.

Please remove this restriction.

@odersky
Copy link

odersky commented Mar 15, 2020

This will be next to impossible to achieve. Opaque types rely on location for type-checking. Inline methods rely on moving code to the caller. I see no way to (re-)typecheck inlined code at the call site and at the same time keep typing as if it was at the original location.

@kwark
Copy link

kwark commented Mar 18, 2020

Does this mean refinement types are next to impossible with scala3? At least using opaque types?

@soronpo
Copy link
Author

soronpo commented Mar 18, 2020

Does this mean refinement types are next to impossible with scala3? At least using opaque types?

They will be possible, but via literal-type operations. We just need to add an Error type for this purpose. See scala/scala3#7951

@neko-kai
Copy link

@kwark You could potentially move your inlines to where opaque type are not visible:

package example
type X = x.X
object X { 
  inline def apply ...
  private[example] object x {
    opaque type X
  }
}

@letalvoj
Copy link

Following @neko-kai example, since the desired behaviour can be achieved by introducing an extra scope which hides the opaque type, would it be possible to use a simmilar approach internally in the compiler?

I can easily see programmers failing on this particular feature while implementing their own refined types.

@gabfssilva
Copy link

I came up with:

import scala.compiletime.*

opaque type Positive = Long

object Positive extends PositiveRefined:
  private[refined] def unsafe(input: Long): Positive = input

trait PositiveRefined:
  def safe(v: Long): Positive | IllegalArgumentException =
    if v >= 0 then Positive.unsafe(v)
    else IllegalArgumentException("value must be >= 0")

  inline def refine(inline v: Long): Positive =
    if v >= 0 then Positive.unsafe(v)
    else error("value must be >= 0")

And, to use it:

val positive: Positive = Positive.refine(1) //compiles just fine
val negative: Positive = Positive.refine(-1) //compilation error: value must be >= 0

val runtimeLong = 10L

Positive.safe(runtimeLong) match
  case p: Positive => ???
  case e: IllegalArgumentException => ???

AFAIK no boxing is being made at runtime whatsoever if we use the refine method

@soronpo
Copy link
Author

soronpo commented Jun 14, 2021

@gabfssilva
FYI, this workaround will soon be disallowed: scala/scala3#12815

@gabfssilva
Copy link

After this PR, the other way around would be this:

opaque type Positive = Long

object Positive:
  private[refined] def unsafe(input: Long): Positive = input

object PositiveRefined:
  def safe(v: Long): Positive | IllegalArgumentException =
    if v >= 0 then Positive.unsafe(v)
    else IllegalArgumentException("value must be >= 0")

  inline def refine(inline v: Long): Positive =
    if v >= 0 then Positive.unsafe(v)
    else error("value must be >= 0")

and then, instead of Positive.refine, we'd use: PositiveRefined.refine.

@odersky
Copy link

odersky commented Jun 15, 2021

Good news: Once scala/scala3#12815 is merged, the restriction will be lifted. So no workarounds are needed anymore.

@soronpo
Copy link
Author

soronpo commented Jun 15, 2021

Thank you so much Martin! It's nice to see a PR going from more restriction to no restriction at all 👍

@johnynek
Copy link

Thank you so much for the work on this @odersky! I think opaque type and inline def go together pretty naturally. Also it's awkward to have language features that don't compose. I think this simplifies the teaching of scala 3.

@soronpo
Copy link
Author

soronpo commented Jun 17, 2021

Fixed by scala/scala3#12815

@soronpo soronpo closed this as completed Jun 17, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants