Skip to content

Instantly share code, notes, and snippets.

@b-studios
Last active December 14, 2016 15:09
Show Gist options
  • Save b-studios/024ce3ff0e262e6eedeac30fb0c2d179 to your computer and use it in GitHub Desktop.
Save b-studios/024ce3ff0e262e6eedeac30fb0c2d179 to your computer and use it in GitHub Desktop.
New encoding for extensible objects
scalaVersion := "2.11.7"
// For this dependency you have to clone
// https://github.com/b-studios/MixinComposition
// and run `sbt publishLocal`
libraryDependencies += "de.unimarburg" % "mixin-composition_2.11" % "0.2-SNAPSHOT"
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value
libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.0.6"
package obj.extend
import de.unimarburg.composition
import de.unimarburg.composition.WithF
object functors {
// reexport type of functor
type Functor[F[_]] = scalaz.Functor[F]
// The top element of the lattice
type AnyF[+_] = AnyRef
def AnyF[S] = (s: S) => new AnyF[S] {}
// Allows spawning a functor instance for the intersection of `C` and `E`
def merge[C[+_], E[+_]](implicit cf: Functor[C], ef: Functor[E], mix: C WithF E) =
new Functor[(C WithF E)#Apply] {
type F[X] = (C WithF E)#Apply[X]
def map[A, B](fa: F[A])(f: A => B): F[B] = mix(cf.map(fa)(f), ef.map(fa)(f))
}
// Converting de.unimarburg.composition.Functor to scalaz.Functor:
implicit def functorConv[F[_]](implicit functor: composition.Functor[F]): scalaz.Functor[F] =
new scalaz.Functor[F] {
def map[A, B](fa: F[A])(f: A => B): F[B] = functor.map(f, fa)
}
def map[F[_], A, B](fa: F[A])(f: A => B)(implicit fun: Functor[F]): F[B] =
fun.map(fa)(f)
}
package obj.extend
import scalaz.std.function.fix
import obj.extend.typetags._
import de.unimarburg.composition.reflection._
// This encoding builds on references.scala - its aim is to remove the need
// for lenses by using mutable state directly.
//
// The core idea of the reference encoding is that Fix acts as box for the
// object to maintain reference equality in.
//
// Thus, val `x: Fix[F]; assert(x.extend(G) eq x)` but !(x.extend(G).out eq x.out)
//
// this should work, since the type parameter to Fix is erased anyway...
object newEncoding {
// We might need this later for references to super.. For self we can
// just use the boxed Obj[F] (Fix[F])
class Ref[T](t: => T) {
private var value: T = null.asInstanceOf[T]
def dereference: T = {
if (value == null) {
value = t
}
value
}
def set(t: T): Unit = value = t
}
def Ref[T](t: => T): Ref[T] = new Ref[T](t)
implicit def autoDeref[T](ref: Ref[T]): T = ref.dereference
// This is now called Mixin, previously known as PreCoAlg
trait Mixin[-Self, +Prov, State] {
def apply: (Obj[X] forSome { type X <: Self }) => State => Prov
}
// Only classes can be instantiated. Previously called CompleteCoAlg
type Class[F, S] = Mixin[F, F, S]
// this is not a real fixed point any more, it's more of a reference container
// (this was previously called Fix, now Obj to match the name obj.extend)
//
// This might be related to the notion of Boxes in
// "Combining traits with boxes and ownership types in a Java-like setting"
// Lorenzo Bettini et al.
trait Obj[+F] {
def out: F
def extend[G, S2](other: Mixin[F with G, G, S2], state: S2)(implicit gT: TypeTag[G]): Obj[F with G]
// this is just a syntax helper
def extend[G: TypeTag](other: Mixin[F with G, G, Unit]): Obj[F with G] = this.extend(other, ())
}
// wrap an arbitrary object to make it extensible
def Obj[F: TypeTag](f: F): Obj[F] = unfold[F, Unit](new Class[F, Unit] {
def apply = self => state => f
}, ())
implicit def autoUnroll[F](f: Obj[F]): F = f.out
// this type casting is always safe at runtime since we only cast to type constructors
// and we maintain the invariant that { TypeTag[X]; _self: X }
def unfold[F, S1](co1: Class[F, S1], s1: S1)(implicit fT: TypeTag[F]): Obj[F] = new Obj[F] { self =>
//type OpenSelf = (=> F) => F
// type: (F) => F -- we have to use Any here to allow later change
//private var openSelf: Any = (f: F) => co1.apply(self)(s1) //co1.apply(self.asInstanceOf[Ref[F]])(s1)
private var _self: F = co1.apply(self)(s1)
// TODO use for `super`
// type: Ref[F]
//private val self: Ref[_] = close
// type: TypeTag[F]
private var typetag: TypeTag[_] = fT
def out = _self
// XXX not used right now, left for `super`
// private def close: Ref[F] = fix[Ref[F]](f => Ref(openSelf.asInstanceOf[OpenSelf](f)))
def extend[G, S2](other: Mixin[F with G, G, S2], state: S2)(implicit gT: TypeTag[G]) = {
val fT = typetag.asInstanceOf[TypeTag[F]]
// here we cast to the already specialized type
val refinedSelf = self.asInstanceOf[Obj[F with G]]
// now actually refine `self`
// XXX is self in co1 bound to refinedSelf? I guess no
_self = mix(fT, gT)(_self, other.apply(refinedSelf)(state))
typetag = intersectionTypeTag(fT, gT)
refinedSelf
}
}
}
object newEncodingTests {
import newEncoding._
import de.unimarburg.composition.reflection._
// Currently this.type is not supported
trait Counter {
def get: Int
def inc: Unit
}
trait SetCounter extends Counter {
def set(n: Int): Unit
}
trait ResetCounter {
def reset: Unit
}
object Counter extends Class[Counter, Int] {
def apply = self => n => new Counter {
private var count = n
def get: Int = count
def inc: Unit = count +=1
}
}
object SetCounter extends Class[SetCounter, Int] {
def apply = self => n => new SetCounter {
private var count = n
def get: Int = count
def set(n: Int) = count = n
def inc = self.set(self.get + 1)
}
}
object ResetCounterRetroFit extends Mixin[ResetCounter with SetCounter, ResetCounter, Unit] {
def apply = self => _ => new ResetCounter {
def reset = self.set(0)
}
}
val c = unfold(Counter, 0)
println(c.get)
c.inc
c.inc
println(c.get)
assert(c.get == 2)
val sc = unfold(SetCounter, 0)
sc.inc
sc.inc
// SetCounter after two times incrementing
assert(sc.get == 2)
val sc2 = sc.extend(ResetCounterRetroFit, ())
val scOut: ResetCounter with SetCounter = sc2.out
scOut.reset
// SetCounter after reset
assert(sc.get == 0)
// reference equality
assert(sc eq sc2)
def Mixin[Self, Prov, State](impl: (Obj[X] forSome { type X <: Self }) => State => Prov) =
new Mixin[Self, Prov, State] {
def apply = self => state => impl(self)(state)
}
val CoolSyntax = Mixin[ResetCounter with SetCounter, ResetCounter, Unit] { self => _ =>
new ResetCounter {
def reset = self.set(0)
}
}
implicit class ClassOps[F: TypeTag, S](cls: Class[F, S]) {
def _new(implicit ev: Unit =:= S) = unfold[F, S](cls, ())
def _new[A](a: A)(implicit ev: A =:= S) = unfold[F, S](cls, a)
def _new[A, B](a: A, b: B)(implicit ev: (A, B) =:= S) = unfold[F, S](cls, (a, b))
def _new[A, B, C](a: A, b: B, c: C)(implicit ev: (A, B, C) =:= S) = unfold[F, S](cls, (a, b, c))
def _new[A, B, C, D](a: A, b: B, c: C, d: D)(implicit ev: (A, B, C, D) =:= S) = unfold[F, S](cls, (a, b, c, d))
}
import scala.language.reflectiveCalls
def implements[Prov] = new {
def apply(impl: (Obj[X] forSome { type X <: Prov }, Unit) => Prov) =
new Mixin[Prov, Prov, Unit] {
def apply = self => state => impl(self, state)
}
def requires[Req] = new {
def apply(impl: (Obj[X] forSome { type X <: Req with Prov }, Unit) => Prov) =
new Mixin[Req with Prov, Prov, Unit] {
def apply = self => state => impl(self, state)
}
}
def takes[State] = new {
def apply(impl: (Obj[X] forSome { type X <: Prov }, State) => Prov) =
new Mixin[Prov, Prov, State] {
def apply = self => state => impl(self, state)
}
}
}
val ResetCounter2 = implements[ResetCounter] requires[SetCounter] { case (self, _) => new ResetCounter {
def reset = self.set(0)
}
}
// In this setting it should be fairly easy to implement the type checking example
trait Exp
trait Lit extends Exp { def n: Int }
trait Add extends Exp { def lhs: Obj[Exp]; def rhs: Obj[Exp] }
val Lit = implements[Lit] takes[Int] { case (self, _n) => new Lit {
def n = _n
}}
val Add = implements[Add] takes[(Obj[Exp], Obj[Exp])] { case (self, (_lhs, _rhs)) => new Add {
def lhs = _lhs
def rhs = _rhs
}}
trait Type
case object NumT extends Type
trait Typed {
def tpe: Type
}
def addType(e: Obj[Exp], t: Type) = e extend (implements[Typed] { case (self, _) => new Typed {
def tpe = t
}}, ())
def typecheck(e: Obj[Exp]): Obj[Exp with Typed] = e.out match {
case l: Lit => addType(e, NumT)
case a: Add => (typecheck(a.lhs).tpe, typecheck(a.rhs).tpe) match {
case (NumT, NumT) => addType(e, NumT)
case _ => sys error "Type Error"
}
}
def lit(n: Int) = unfold(Lit, n)
def add(l: Obj[Exp], r: Obj[Exp]) = unfold(Add, (l, r))
val tree = add(add(add(lit(1), lit(2)), lit(3)), add(lit(4), lit(5)))
println(typecheck(tree).tpe)
// Pure awesomeness, we can lift any value to be extensible with this encoding?
// XXX To actually support case classes we need to improve the compose
// mechanism. Currently no params are passed to constructor calls
implicit def makeExtensible[F: TypeTag](f: F): Obj[F] = Obj[F](f)
locally {
case class Add(lhs: Obj[Exp], rhs: Obj[Exp]) extends Exp
case class Lit(n: Int) extends Exp
implicit def toFix(e: Exp): Obj[Exp] = Obj[Exp](e)
def typecheck(e: Obj[Exp]): Obj[Exp with Typed] = e.out match {
case l: Lit => addType(e, NumT)
case a: Add => (typecheck(a.lhs).tpe, typecheck(a.rhs).tpe) match {
case (NumT, NumT) => addType(e, NumT)
case _ => sys error "Type Error"
}
}
val tree: Obj[Exp] = Add(Add(Add(Lit(1), Lit(2)), Lit(3)), Add(Lit(4), Lit(5)))
println(typecheck(tree).tpe)
println(tree.asInstanceOf[Obj[Typed]].tpe)
}
trait OverrideMe {
def test: String
}
trait Printer {
def print: String
}
locally {
val BasePrinter = implements[Printer with OverrideMe] { case (self, _) => new Printer with OverrideMe {
def test = "Base"
def print = s"self: ${ test }, late: ${ self.test }"
}}
val OverridePrinter = implements[OverrideMe] requires[Printer] { case (self, _) => new OverrideMe {
def test = "Override1"
}}
val OverridePrinter2 = implements[OverrideMe] requires[Printer] { case (self, _) => new OverrideMe {
def test = "Override2"
}}
val base = BasePrinter._new
assert(base.print == "self: Base, late: Base")
base extend OverridePrinter
assert(base.print == "self: Base, late: Override1")
base extend OverridePrinter2
assert(base.print == "self: Base, late: Override2")
// only takes 20ms, due to reduced reflection!
measure("Printing 10.000 times") {
for (i <- 1 to 10000) {
val x = base.print
}
}
def measure[T](text: String)(block: => T): T = {
val before = System.currentTimeMillis
val result = block
val after = System.currentTimeMillis
println(s"$text took " + (after - before) + "ms")
result
}
}
// Some rails like use cases
trait DomainItem { def name: String }
def DomainItem(n: String) = new DomainItem { def name = n }
trait Helpers {
def url: String
}
locally {
val Helpers = implements[Helpers] requires[DomainItem] { case (self, _) => new Helpers {
def url = s"http://www.myDomain.com/${ self.name }"
}}
def render(d: DomainItem): String = {
// look at this syntax:
val item = d extend Helpers
s"""<html><body><a href="${ item.url }">link</a></body></html>"""
}
println(render(DomainItem("FooBar")))
}
// From Yehuda Katz blogpost: http://yehudakatz.com/2009/11/12/better-ruby-idioms/
trait YaffleMethods {
def foo: Int
}
val YaffleMethods = implements[YaffleMethods] { case (self, _) => new YaffleMethods {
def foo = 42
}}
trait Yaffle {
def acts_as_something: Obj[YaffleMethods]
}
val Yaffle = implements[Yaffle] { case (self, _) => new Yaffle {
def acts_as_something = self extend YaffleMethods
}}
// to be used as
trait ActiveRecord { def bar: Int}
val ActiveRecord = implements[ActiveRecord] requires[Yaffle] { case (self, _) => new ActiveRecord {
// this is only needed since we cannot tell Scala that self is now also of type Yaffle
// so we consciously shadow self
def bar = self.acts_as_something match { case self =>
self.foo
}
}}
// TODO use <*> here. We also need facilities to immediately specify superclasses, like
// val ActiveRecord = implements[ActiveRecord] extend(Yaffle)
val ac = Yaffle._new extend ActiveRecord
println(ac.bar)
}
package obj
import scala.reflect.runtime.universe.{ TypeTag => RuTypeTag, WeakTypeTag => RuWeakTypeTag, _}
import scalaz.{ Lens, Functor }
import scalaz.LensFamily.{ lensId, firstLens, secondLens }
import scalaz.std.function.fix
import scalaz.syntax._
import de.unimarburg.composition.WithF
import de.unimarburg.composition.reflection._
import obj.extend.typetags._
import obj.extend.functors._
package object extend {
// enable implicit conversions and higher kinds for the whole project
private[extend] implicit val enableHigherKinds = scala.language.higherKinds
private[extend] implicit val enableImplicits = scala.language.implicitConversions
// alias to be imported automatically at user site
type TypeTag[T] = RuTypeTag[T]
type WeakTypeTag[T] = RuWeakTypeTag[T]
type CoAlg[+F[+_], S] = S => F[S]
// Following Oliveira's development on adding self references, but also adding
// open references to `super`
type Open[-Super, -Self <: Prov, +Prov] = (=> Super) => (=> Self) => Prov
// The general shape of a coalgebra (instead of distinguishing co and
// contravariant occurrences (as in pre algebras) we use an arbitrary
// functor taking the result type as argument
trait GenCoAlg[+F[_], R] {
def apply[S](l: Lens[S, R]): F[S]
}
/**
* A coalgebra polymorphic in the type of the state `S`
* In order to allow composition with other coalgebras the state is
* accessed via a lens. This way the state always is local to one
* coalgebra but can be passed as the whole frame.
*/
// type OpenCoAlg[-Sup[+_], -Self[+X] <: Prov[X], +Prov[+_], R] =
// GenCoAlg[({ type λ[X] = Open[CoAlg[Sup, X], CoAlg[Self, X], CoAlg[Prov, X]] })#λ, R]
trait OpenCoAlg[-Base[+_], -Self[+X] <: Prov[X], +Prov[+_], S] {
def apply[T](l: Lens[T, S]): CoAlg[Base, T] => CoAlg[Self, T] => CoAlg[Prov, T]
}
/**
* A complete coalgebra provides what it requires and does not depend on
* a `super` coalgebra.
*/
type CompleteCoAlg[F[+_], R] = OpenCoAlg[AnyF, F, F, R]
trait Fix[+F[+_]] {
def out: F[Fix[F]]
def extend[G[+_]: Functor, S2](co2: OpenCoAlg[F, (F WithF G)#Apply, G, S2], state2: S2)(
implicit gT: TypeTag[G[S2]]): Fix[(F WithF G)#Apply]
}
/**
* Mechanism to compose to OpenCoAlgs
*
* Required and provided interfaces are aggregated in intersection types. This
* and the later fixed point construction allow mutual recursive dependencies
* between OpenCoAlgebras.
*/
def compose[
Base1[+X], Self1[+X] <: Prov1[X], Prov1[+_], S1,
Self2[+X] <: Prov2[X], Prov2[+_], S2](
co1: OpenCoAlg[Base1, Self1, Prov1, S1],
co2: OpenCoAlg[(Base1 WithF Prov1)#Apply, Self2, Prov2, S2])(
implicit
mix1: Base1 WithF Prov1,
mix2: Prov1 WithF Prov2) =
new OpenCoAlg[Base1, (Self1 WithF Self2)#Apply, (Prov1 WithF Prov2)#Apply, (S1, S2)] {
def apply[T](priv: Lens[T, (S1, S2)]) = base => self => state => {
// This one allows transitive access to all parents, as opposed to the
// simpler :
// val left = co1[T](priv andThen firstLens)(base)(self)
val left = (s: T) => mix1[T](
base(s),
co1[T](priv andThen firstLens)(base)(self)(s)
)
val right = co2[T](priv andThen secondLens)(left)(self)
mix2[T](left(state), right(state))
}
}
// fixed point of coalgebras (closing the self reference)
// The super reference is a dummy object
def close[F[+_], S](co: CompleteCoAlg[F, S]): CoAlg[F, S] =
fix[CoAlg[F, S]] {
self => state => co[S](lensId)(s => new {})(self)(state)
}
/**
* This function is at the heart of the thesis. At every layer of unfolding
* the extension point is inserted
*
* The implementation looks scary. This is only due to all the magic
* necessary to make type tags work. While reading simply skip everything
* that is related to `TypeTag`.
*
* Almost all `implicit val`s can be omitted by then `TypeTag[Nothing]` is
* inferred too often leading to the error:
*
* scala.tools.reflect.ToolBoxError: reflective toolbox failed due to
* unresolved free type variables
*
* So if you see this error, try adding some manually crafted type tags
* to the implicit resolution scope.
*/
def unfold[F[+X] <: AnyF[X] : Functor, S1](co1: CompleteCoAlg[F, S1])(
// this way we get both type tags, F[_] and S
implicit fT: TypeTag[F[S1]]): S1 => Fix[F] = state1 => new Fix[F] {
// Here we only close the self reference, but this holds only for the known
// branch of the tree. The (?) branch still has a reference to the open coalgebra
lazy val out = map(close(co1)(state1))(unfold(co1))
def extend[G[+_]: Functor, S2](co2: OpenCoAlg[F, (F WithF G)#Apply, G, S2], state2: S2)(
implicit gT: TypeTag[G[S2]]): Fix[(F WithF G)#Apply] = {
// All of this is hidden in the thesis
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
type BothS = (S1, S2)
type BothF[+X] = (F WithF G)#Apply[X]
// type tags needed for merging the functors and for materializing F WithF G
implicit val fWildcard: WeakTypeTag[F[_]] = wildcardTypeTag[F, S1]
implicit val gWildcard: WeakTypeTag[G[_]] = wildcardTypeTag[G, S2]
// Type tags necessary for recursive call
// --------------------------------------
// extracted type tag for S
implicit val sT: TypeTag[S1] = typetags.unapplyTypeTag(fT)
implicit val s2T: TypeTag[S2] = typetags.unapplyTypeTag(gT)
// type tags for the composed interfaces and states
implicit val bothST = implicitly[TypeTag[(S1, S2)]]
implicit val fBothT: TypeTag[F[BothS]] = typetags.appliedTypeTag[F, S1, BothS]
implicit val gBothT: TypeTag[G[BothS]] = typetags.appliedTypeTag[G, S2, BothS]
implicit val bothFT = intersectionTypeTag[F[BothS], G[BothS]]
// instance of the composed functor
implicit val bothFunctors: Functor[(F WithF G)#Apply] = merge[F, G]
// this is Base1 in compose is AnyF, so we need mixF[AnyF, F]
val mix1 = WithF.anyL[F]
val mix2 = implicitly[F WithF G]
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
unfold[(F WithF G)#Apply, (S1, S2)](
compose[AnyF, F, F, S1, (F WithF G)#Apply, G, S2](co1, co2)(mix1, mix2)
).apply((state1, state2))
}
}
}
package obj.extend
import scalaz.{ Lens, Functor, State }
import de.unimarburg.composition.WithF
import obj.extend.functors._
package object syntax {
trait Mixin[Sup[+_], Sel[+X] <: Pro[X], Pro[+_], R] extends OpenCoAlg[Sup, Sel, Pro, R] {
type State = R
type Super[+X] = Sup[X]
type Self[+X] = Sel[X]
type Prov[+X] = Pro[X]
def apply[S](l: Lens[S, R]) = sup => self => impl(l, sup, self)
def impl[S](implicit l: Lens[S, R], sup: CoAlg[Sup, S], self: CoAlg[Self, S]): CoAlg[Prov, S]
// trying to improve the interface on lenses
implicit class LensOps[S, R](lens: Lens[S, R])(implicit state: S) {
def ? = lens.get(state)
def :=(newR: R): S = lens.set(state, newR)
}
implicit def applyState[S, R](st: scalaz.State[S, R])(implicit s: S): S = st(s)._1
implicit def applyFun[S](f: S => S)(implicit s: S): S = f(s)
implicit def liftIntoLens[S](f: R => R)(implicit l: Lens[S, R], s: S) = l.mod(f, s)
implicit def autoState[S, F[+_]](coalg: CoAlg[F, S])(implicit s: S): F[S] = coalg(s)
}
// Syntax for defining pre co algs without having to give explicit types
trait Mixin2[Sup[+_], Sel[+X] <: Pro[X], Pro[+_], R] extends OpenCoAlg[Sup, Sel, Pro, R] {
def impl[S]: Lens[S, R] => CoAlg[Sup, S] => CoAlg[Sel, S] => CoAlg[Pro, S]
def apply[S](l: Lens[S, R]) = sup => self => impl(l)(sup)(self)
}
trait CompleteMixin2[Pro[+X] <: AnyF[X], R] extends Mixin2[AnyF, Pro, Pro, R] {
def _new(s: R)(implicit f: Functor[Pro], ft: TypeTag[Pro[R]]) =
unfold[Pro, R](this).apply(s)
}
implicit class CoAlgOps[F[+X] <: AnyF[X], S](co: CompleteCoAlg[F, S]) {
def _new(s: S)(implicit f: Functor[F], ft: TypeTag[F[S]]) = unfold[F, S](co).apply(s)
}
implicit class OpenCoAlgOps[Base1[+X], Self1[+X] <: Prov1[X], Prov1[+_], S1](self: OpenCoAlg[Base1, Self1, Prov1, S1]) {
def <+>[Self2[+X] <: Prov2[X], Prov2[+_], S2](other: OpenCoAlg[(Base1 WithF Prov1)#Apply, Self2, Prov2, S2])(
implicit mix1: Base1 WithF Prov1, mix2: Prov1 WithF Prov2) = compose[Base1, Self1, Prov1, S1, Self2, Prov2, S2](self, other)
}
// should probably be: implicit def unroll[F[+X] <: G[X], G[+_]](f: Fix[F]): G[Fix[F]] = f.out
// but this wont work
implicit def unroll[F[+X]](f: Fix[F]) = f.out
}
package obj.extend
// println statements are stashed in comments to debug type tag resolution
object typetags {
import scala.reflect.runtime.universe.{ appliedType, runtimeMirror, typeTag, typeOf, TypeTag, Type, RefinedType, WildcardType }
import scala.reflect.api.{ TypeCreator, Universe => ApiUniverse, Mirror }
import scala.tools.reflect.ToolBox
def typeTagForType[T](t: Type): TypeTag[T] = {
val mirror = runtimeMirror(getClass.getClassLoader)
TypeTag[T](mirror, new TypeCreator {
def apply[U <: ApiUniverse with Singleton](m: Mirror[U]): U # Type = {
val u = m.universe
assert(m == mirror)
// this only works if the mirror does not change...
// println("creating typetag for " + t.toString)
t.asInstanceOf[U#Type]
}
})
}
// Seems like this is the only necessary implicit
def unapplyTypeTag[F[+_], S](implicit fT: TypeTag[F[S]]): TypeTag[S] = {
def collectArgs(tpe: Type): Set[Type] = tpe match {
case t if t =:= typeOf[Nothing] => sys error "Got TypeTag[Nothing]"
case RefinedType(components, _) => components.toSet.flatMap { collectArgs }
case t => Set(t.typeArgs.head)
}
val args = collectArgs(fT.tpe)
if (args.size != 1) {
sys error "Different type arguments: " + args
}
typeTagForType[S](args.head)
}
def intersectionTypeTag[A: TypeTag, B: TypeTag]: TypeTag[A with B] = {
val res = typeTag[A with B]
//println("intersection " + res.toString)
res
}
// applies the type constructor F to S
// X is a type that needs to be ignored. Using wildcards here does not work
def appliedTypeTag[F[_], X, S](implicit fT: TypeTag[F[X]], sT: TypeTag[S]): TypeTag[F[S]] = {
//println("applied type tag for " + fT.toString + " and " + sT.toString)
val res = typeTagForType[F[S]](appliedType(fT.tpe.typeConstructor, sT.tpe))
//println(res)
res
}
def wildcardTypeTag[F[_], S](implicit fT: TypeTag[F[S]]): TypeTag[F[_]] = {
//println("wildcard type tag for " + fT.toString)
val res = typeTagForType[F[_]](appliedType(fT.tpe.typeConstructor, WildcardType))
//println(res)
res
}
def tupleTypeTag[A, B](implicit fst: TypeTag[A], snd: TypeTag[B]): TypeTag[(A, B)] =
implicitly
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment