Skip to content

Instantly share code, notes, and snippets.

@pjrt
Last active October 2, 2020 10:24
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save pjrt/269ddd1d8036374c648dbf6d52fb388f to your computer and use it in GitHub Desktop.
Save pjrt/269ddd1d8036374c648dbf6d52fb388f to your computer and use it in GitHub Desktop.
A typeclass box: example usage of Existential Types in Scala.
import scala.language.implicitConversions
import scala.language.higherKinds
/**
* A simple typeclass that converts types A to some specific Java type
*/
trait AllowedType[A] {
type JavaType >: Null <: AnyRef
def toJavaType(a: A): JavaType
def toObject(a: A): Object = toJavaType(a)
}
object AllowedType {
def apply[A](implicit ev: AllowedType[A]): AllowedType[A] = ev
/**
* Helper instance creator
*/
def mkInstance[A, J >: Null <: AnyRef](f: A => J): AllowedType[A] =
new AllowedType[A] {
type JavaType = J
def toJavaType(a: A) = f(a)
}
implicit val intInstance: AllowedType[Int] =
mkInstance(Int.box(_))
implicit val strInstance: AllowedType[String] =
mkInstance(identity)
implicit val boolInstance: AllowedType[Boolean] =
mkInstance(Boolean.box(_))
// We can create an instance for TCBox itself! It is simply the inner
// type's instance.
implicit def instanceForAny: AllowedType[TCBox[AllowedType]] =
mkInstance(ev => ev.evidence.toJavaType(ev.value))
}
// data TCBox tc = forall a. tc a => TCBox a
sealed trait TCBox[TC[_]] {
type T
val value: T
val evidence: TC[T]
}
private case class MkTCBox[A, TC[_]](value: A)(implicit val evidence: TC[A])
extends TCBox[TC] {
type T = A
}
object TCBox {
def apply[T, TC[_]](value: T)(implicit ev: TC[T]): TCBox[TC] =
MkTCBox(value)
/**
* Allows for case matching with evidence
*/
def unapply[TC[_]](t: TCBox[TC]): Option[(t.T, TC[t.T])] =
Some(t.value -> t.evidence)
// If `A` implements the type class `TC`, then `A` can be wrapped by
// `TCBox[TC]` if it is expected. This allows for automatic lifting of
// types into TCBox.
implicit def anyToBox[TC[_], A: TC](v: A): TCBox[TC] = TCBox(v)
}
object Example {
def bind(objs: Object*): Unit = ()
type AnyAllowedType = TCBox[AllowedType]
def toObj(t: AnyAllowedType) = AllowedType[AnyAllowedType].toObject(t)
def bindT(objs: AnyAllowedType*): Unit =
bind(objs.map(toObj):_*)
def f = bindT(123, "Hello", true)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment