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

Support Array casting #10

Open
carymrobbins opened this issue Mar 1, 2018 · 2 comments
Open

Support Array casting #10

carymrobbins opened this issue Mar 1, 2018 · 2 comments

Comments

@carymrobbins
Copy link
Member

@alexknvl pointed out that it's possible to encode newtypes in such a way that casting Arrays with asInstanceOf will work (which means Coercible can work too).

Here's a simple adaptation from his solution to demonstrate a failing test case.

object Example {

  class Foo[A](val a: Array[A])

  val foo = new Foo(Array(1, 2, 3))

  def subst[F[_], T](fa: F[Int]): F[T] = fa.asInstanceOf[F[T]]

  type Good = Good.Type
  object Good {
    type Base <: Any
    trait Tag extends Any
    type Type <: Base with Tag
  }

  def good() = println(subst[Foo, Good](foo).a.head)

  @newtype case class Bad(value: Int)

  def bad() = println(subst[Foo, Bad](foo).a.head)

  def main(args: Array[String]): Unit = {
    good()
    bad()
  }
}
@carymrobbins
Copy link
Member Author

/cc @joroKr21

The encoding of the newtype macros in v0.4.0 now allow for asInstanceOf casting by having the Base and Tag types extend Any, but of course this must be done with caution. Any time you use asInstanceOf you lose type safety, so it's important that you understand what you're doing.

scala> Array(1,2,3).asInstanceOf[Array[Foo]]
res0: Array[Foo] = Array(1, 2, 3)

scala> res0.getClass
res1: Class[_ <: Array[Foo]] = class [I

scala> res0.head
res2: Foo = 1

I have also started work on the 10-as-array branch which introduces the AsArray type class.

scala> import io.estatico.newtype.macros._, io.estatico.newtype.arrays._

scala> @newtype case class Foo(x: Int)

scala> AsArray(Foo(1), Foo(2))
res0: Array[Foo] = Array(1, 2)

scala> res0.getClass
res1: Class[_ <: Array[Foo]] = class [I

scala> res0.head
res2: Foo = 1

scala> AsArray.empty[Foo]
res3: Array[Foo] = Array()

scala> res3.getClass
res4: Class[_ <: Array[Foo]] = class [I

scala> AsArray.downcast(res0)
res8: Array[Int] = Array(1, 2)

scala> res8.head
res9: Int = 1

scala> Array(1,2,3)
res10: Array[Int] = Array(1, 2, 3)

scala> AsArray.upcast(res10): Array[Foo]
res12: Array[Foo] = Array(1, 2, 3)

scala> AsArray.upcast[Foo](res10)
res13: Array[Foo] = Array(1, 2, 3)

@carymrobbins
Copy link
Member Author

I think we might be able to now support .coerce for Arrays. Should probably do extensive testing to ensure the current encoding won't blow up on us. If that's the case, then we should be able to omit the upcast and downcast methods on AsArray, leaving only the need for empty and apply.

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

1 participant