Skip to content

Instantly share code, notes, and snippets.

@nicolasstucki
Last active July 14, 2017 10:51
Show Gist options
  • Save nicolasstucki/02eddd8f67f2652b049b3500e20db475 to your computer and use it in GitHub Desktop.
Save nicolasstucki/02eddd8f67f2652b049b3500e20db475 to your computer and use it in GitHub Desktop.
import scala.annotation.implicitNotFound
import scala.reflect.ClassTag

object Test {

  import Phantom_=::=._
  //  import Normal_=::=._

  def main(args: Array[String]): Unit = {
    foo(1, 2)
    foo2(Array(1), Array(1))
    bar(Array(1))
    bar2(1)
  }

  def foo[A, B](a: A, b: B)(implicit ev: =::=[A, B]): B = {
    ev.apply(a)
    SafeCast(a)
  }

  def foo2[A, B](a: Array[A], b: Array[B])(implicit ev: =::=[A, B]) = {
    b(0) = ev(a.head)
    b(0) = SafeCast(a.head)
  }

  def bar[C](a: Array[C])(implicit ev: =::=[C, Int]) = foo2(a, Array(2))

  def bar2[C: ClassTag](a: C)(implicit ev: =::=[C, Int]) = foo2(Array(a), Array(2))

}

object Normal_=::= {
  @implicitNotFound(msg = "Cannot prove that ${From} =:= ${To}.")
  sealed abstract class =::=[From, To] extends (From => To) with Serializable

  private[this] final val singleton_=::= = new =::=[Any, Any] { def apply(x: Any): Any = x }

  object =::= {
    implicit def tpEquals[A]: A =::= A = singleton_=::=.asInstanceOf[A =::= A]
  }
}

object Phantom_=::= extends Phantom {
  @implicitNotFound(msg = "Cannot prove that ${From} =:= ${To}.")
  type =::=[A,B] <: this.Any

  implicit inline def tpEquals[A]: A =::= A = assume

  implicit class Cast[From, To](ev: From =::= To) { // Could be a AnyVal if we support them for Phantom
    inline def apply(a: From): To = a.asInstanceOf[To] // safe cast, we have an evidence to prove it
  }

  object SafeCast { // 0 overhead on application side
    inline def apply[From, To](a: From)(implicit ev: From =::= To): To = a.asInstanceOf[To]
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment