Skip to content

Instantly share code, notes, and snippets.

@DmytroMitin
Forked from kamilkloch/DefaultType.scala
Created May 23, 2020 08:10
Show Gist options
  • Save DmytroMitin/174a6e0817bbe4427e72c077fb8ece10 to your computer and use it in GitHub Desktop.
Save DmytroMitin/174a6e0817bbe4427e72c077fb8ece10 to your computer and use it in GitHub Desktop.
DefaultType
import scala.reflect.ClassTag
object DefaultType extends App {
case class AttributeMeasure[T](name: String)(implicit ev: reflect.ClassTag[T]) {
def ct: ClassTag[T] = ev
}
/** This almost works, except for the case:
* {{{
* val x: AttributeMeasure[Int] = attribute("x")
*
* polymorphic expression cannot be instantiated to expected type;
* found : [T]AttributeMeasure[ev.Out]
* required: AttributeMeasure[Int]
* }}} */
def attribute[T: reflect.ClassTag](name: String)(implicit ev: Default[T, Double]): AttributeMeasure[ev.Out] = AttributeMeasure[ev.Out](name)(ev.ct)
/** This version requires 2 explicit type parameters */
def attribute2[Out, T: reflect.ClassTag](name: String)(implicit ev: Default.Aux[T, Double, Out]): AttributeMeasure[Out] = AttributeMeasure[Out](name)(ev.ct)
/** Intermediate class - workaround for type currying */
class PartiallyAppliedAttribute[T] {
def apply[Out](name: String)(implicit ev: Default.Aux[T, Double, Out], ct: reflect.ClassTag[T]): AttributeMeasure[Out] = AttributeMeasure[Out](name)(ev.ct)
}
/** User-facing method, captures only T. */
def attributePartiallyApplied[T]: PartiallyAppliedAttribute[T] = new PartiallyAppliedAttribute[T]
/**
* @tparam T - input type
* @tparam U - default fallback type
*/
sealed trait Default[T, U] {
/** Output type:
* - U, if T == Nothing
* - T, otherwise
*/
type Out
def ct: ClassTag[Out]
}
object Default extends LowPriorityDefault {
/** Captures Default#Out as a separate type parameter, which can be used as function return type. */
type Aux[T, U, Out0] = Default[T, U] {type Out = Out0}
/** Case when T == Nothing */
implicit def nothingDefault[U](implicit ev: ClassTag[U]): Default.Aux[Nothing, U, U] = new Default[Nothing, U] {
type Out = U
def ct: ClassTag[Out] = ev
}
}
trait LowPriorityDefault {
/** Case when T is explicitly provided */
implicit def explicitDefault[T, U](implicit ev: ClassTag[T]): Default.Aux[T, U, T] = new Default[T, U] {
type Out = T
def ct: ClassTag[Out] = ev
}
}
val g11 = attribute("hi")
val g12 = attribute2("hi")
val g13 = attributePartiallyApplied("hi")
println(g11.ct)
println(g12.ct)
println(g13.ct)
// val g21: AttributeMeasure[Int] = attribute("xx") // does not compile
val g22: AttributeMeasure[Int] = attribute2("xx")
val g23: AttributeMeasure[Int] = attributePartiallyApplied("xx")
println(g22.ct)
println(g23.ct)
val g31 = attribute[String]("xx")
val g32 = attribute2[String, String]("xx")
val g33 = attributePartiallyApplied[String]("xx")
println(g31.ct)
println(g32.ct)
println(g33.ct)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment