How to check I'm inside a @specialized function or class at runtime in scala?
Asked Answered
I

1

5

Let's say I have a specialized class and an associated companion object:

trait Slice[@specialized +T] {
    ...

    override def equals(that :Any) = that match {
        case s :Slice[_] => ???
        case _ => false
    }
}

object Slice {
    def newInstance[@specialized T] = ???
}

Is there any way to check

  1. Inside a method of Slice if this instance is a specialized subclass,
  2. Inside a method of Slice if another instance is a specialized subclass for the same primitive,
  3. Inside a specialized method on a companion object if I'm running an erased or specialized variant

without resorting to ClassTags or passing Class[_] manually? It seems like that information should be available, but the only way I can think of involves checking names of the classes.

Use case 2) is particularly important, as I could resort to faster algorithms if I knew I'm comparing apples with apples. It probably could be accomplished by reflection, but it would be quite tricky when you take into account that we have to handle non-synthetic subclasses of Slice, too; if we have also

trait ArraySlice[@specialized T] extends Slice[T] { ... }

that should be considered 'compatible' with Slice[T] instances as long as they are both specialized (or both erased)?

Insolvency answered 27/11, 2015 at 19:59 Comment(2)
Do you really want to check from within the function? Any check will eat up all performance benefit of specialization. Also, what's wrong with checking class names x.getClass.getName.endsWith("$sp") ? I use this in some unit tests to make sure that specialization works (but from the outside)Burtis
Not if the check and cast is performed on a collection and would eliminate boxing done in a loop. The problem with checking class nake suffix is that inspected instance might be a specialization of a subtrait, extending the erased version of this trait. Add nested classes, and such code would be very tricky to get right, never mind brittle dependency on the naming scheme used by the compiler.Insolvency
I
2

Ok, I figured out a cleaner way:

final val AllButUnit = new Specializable.Group((Byte, Short, Int, Long, Char, Float, Double, Boolean, AnyRef))

def specializationFor[@specialized(AllButUnit) E] :ResolvedSpecialization[E] =
    Specializations(new SpecializedKey[E]).asInstanceOf[ResolvedSpecialization[E]]


private val Specializations = Seq(
    resolve[Byte],
    resolve[Short],
    resolve[Int],
    resolve[Long],
    resolve[Char],
    resolve[Float],
    resolve[Double],
    resolve[Boolean],
    resolve[Unit],
    resolve[AnyRef]
).map(
    spec => spec.key -> spec :(SpecializedKey[_], ResolvedSpecialization[_])
).toMap.withDefaultValue(resolve[AnyRef])

private def resolve[@specialized(AllButUnit) E :ClassTag] :ResolvedSpecialization[E] =
    new ResolvedSpecialization[E](new SpecializedKey[E], new Array[E](0))


class ResolvedSpecialization[@specialized(AllButUnit) E] private[SpecializedCompanion]
        (val array :Array[E], val elementType :Class[E], val classTag :ClassTag[E], private[SpecializedCompanion] val key :SpecializedKey[E])
{
    private[SpecializedCompanion] def this(key :SpecializedKey[E], array :Array[E]) =
        this(array, array.getClass.getComponentType.asInstanceOf[Class[E]], ClassTag(array.getClass.getComponentType.asInstanceOf[Class[E]]), key)

    override def toString = s"@specialized($elementType)"

    override def equals(that :Any) = that match {
        case r :ResolvedSpecialization[_] => r.elementType==elementType
        case _ => false
    }

    override def hashCode = elementType.hashCode
}

private class SpecializedKey[@specialized(AllButUnit) E] {
    override def equals(that :Any) = that.getClass==getClass
    override def hashCode = getClass.hashCode

    def className = getClass.getName
    override def toString = className.substring(className.indexOf("$")+1)
}

Now specializationFor[E].elementType returns class corresponding to specialization parameter of E.

Insolvency answered 30/11, 2015 at 12:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.