Created
May 22, 2020 21:45
-
-
Save kamilkloch/a5c97d0c7cdec47f8dc4c4ac4c131674 to your computer and use it in GitHub Desktop.
DefaultType
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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