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

newtype extending newtype when underlying values extend eachother? #66

Closed
s5bug opened this issue Jun 2, 2020 · 5 comments
Closed

newtype extending newtype when underlying values extend eachother? #66

s5bug opened this issue Jun 2, 2020 · 5 comments

Comments

@s5bug
Copy link

s5bug commented Jun 2, 2020

Given:

trait Foo
trait Bar extends Foo

why wouldn't

@newtype class NFoo(val internal: Foo)
@newtype class NBar(override val internal: Bar) extends NFoo(internal)

be valid?

@s5bug
Copy link
Author

s5bug commented Jun 2, 2020

Here's how it might be expanded:

type NFoo = NFoo.Type
object NFoo {
  type Repr = Foo
  type Base = Any { type NFoo$newtype }
  trait Tag extends Any
  type Type <: Base with Tag
  implicit final class Ops$newtype(val $this$: Type) extends AnyVal {
    def internal: Foo = $this$.asInstanceOf[Foo]
  }
}

type NBar = NBar.Type
object NBar {
  type Repr = Bar
  type Base = Any { type NBar$newtype }
  trait Tag extends Any
  type Type <: NFoo.Type with Base with Tag
  implicit final class Ops$newtype(val $this$: Type) extends AnyVal {
    def internal: Bar = $this$.asInstanceOf[Bar]
  }
}

My only worry is the internals conflicting; depending on how implicit priority works, NBar.Ops$newtype should take priority over NFoo.Ops$newtype.

@s5bug
Copy link
Author

s5bug commented Jun 2, 2020

I have verified that the implicit ops don't conflict with each other. Taking the code above and using it to do:

def doTheThingFoo(nfoo: NFoo): Foo = nfoo.internal
def doTheThingBar(nbar: NBar): Bar = nbar.internal

lazy val myBar: Bar = ???
doTheThingFoo(myBar.asInstanceOf[NBar])

compiles just fine.

I don't yet have an easy way to test if it works at runtime, but I don't see why it wouldn't.

@carymrobbins
Copy link
Member

Given:

trait Foo
trait Bar extends Foo

why wouldn't

@newtype class NFoo(val internal: Foo)
@newtype class NBar(override val internal: Bar) extends NFoo(internal)

be valid?

I see what you mean. However, I'm a bit hesitant to make the encoding even more complex. However, you can get these same semantics with the following -

import io.estatico.newtype.macros.newtype

object Main {

  class Foo
  class Bar extends Foo

  @newtype case class N[+A](internal: T forSome { type T <: A })

  type NFoo = N[Foo]
  val NFoo: Foo => NFoo = N.apply

  type NBar = N[Bar]
  val NBar: Bar => NBar = N.apply

  def printNFoo(nfoo: NFoo): Unit = println(nfoo)
  def printNBar(nbar: NBar): Unit = println(nbar)

  def main(args: Array[String]): Unit = {
    val foo: Foo = new Foo
    val bar: Bar = new Bar
    val nfoo: NFoo = NFoo(foo)
    val nbar: NBar = NBar(bar)

    // Of course this works
    printNFoo(nfoo)

    // Works because NBar is a subtype of NFoo
    printNFoo(nbar)

    // Does not compile because NFoo is not a subtype of NBar
    // printNBar(nfoo)

    // Of course this works
    printNBar(nbar)
  }
}

@s5bug
Copy link
Author

s5bug commented Jun 13, 2020

Can I make @newtype case class N[+A <: Foo]?

@carymrobbins
Copy link
Member

I think so; try adding the <: Foo bound on my previous example -

@newtype case class N[+A <: Foo](internal: T forSome { type T <: A })

@s5bug s5bug closed this as completed Jun 13, 2020
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

No branches or pull requests

2 participants