Get companion object of class by given generic type Scala
Asked Answered
P

5

31

What I am trying to do is to make a function that would take a generic class and use a static method in it (sorry for Java language, I mean method of its companion object).

trait Worker {def doSth: Unit}

class Base

object Base extends Worker

// this actually wouldn't work, just to show what I'm trying to achieve
def callSthStatic[T that companion object is <: Worker](implicit m: Manifest[T]) {
  // here I want to call T.doSth (on T object)
  m.getMagicallyCompanionObject.doSth
}

Any ideas?

Procedure answered 7/2, 2012 at 8:0 Comment(0)
R
6

I keep hitting this page when I forget how to do this and the answers are not hundred percent satisfactory for me. Here is how I do with reflection:

val thisClassCompanion = m.reflect(this).symbol.companion.asModule
val structural = m.reflectModule(thisClassCompanion)
                  .instance.asInstanceOf[{def doSth: Unit}]

You might need to verify that the class actually has a companion object or companion.asModule will throw a reflection exception is not a module

Updated: added another example for clarity:

    object CompanionUtil {

  import scala.reflect.runtime.{currentMirror => cm}

  def companionOf[T, CT](implicit tag: TypeTag[T]): CT = {
    Try[CT] {
      val companionModule = tag.tpe.typeSymbol.companion.asModule
      cm.reflectModule(companionModule).instance.asInstanceOf[CT]
    }
  }.getOrElse(throw new RuntimeException(s"Could not get companion object for type ${tag.tpe}"))

}
Riant answered 3/5, 2018 at 16:29 Comment(4)
What is m? Can you give a complete example? I am looking for something I can use like this: abstract class Whatever[T] = { val companion = companionOf[T] }Coranto
m is the current mirror, I get it just renaming an import import scala.reflect.runtime.{currentMirror => m}Riant
Note that CT is the type of the companion object which is T.type, e.g. companionOf[Foo, Foo.type] where Foo is the name of your class that has a companion object.Almita
Based on the answer, here's a method that gets the type reference of the companion object of the specified class object: <br/> def companionOf[CT](clazz: Class[_]): CT = { import scala.reflect.runtime.{currentMirror => cm} Try[CT] { val companionModule = cm.classSymbol(clazz).companion.asModule cm.reflectModule(companionModule).instance.asInstanceOf[CT] }.getOrElse(throw new RuntimeException(s"Could not get companion object for $clazz")) }Liliuokalani
C
25

A gist by Miles Sabin may give you a hint:

trait Companion[T] {
  type C
  def apply() : C
}

object Companion {
  implicit def companion[T](implicit comp : Companion[T]) = comp()
}

object TestCompanion {
  trait Foo

  object Foo {
    def bar = "wibble"

    // Per-companion boilerplate for access via implicit resolution
    implicit def companion = new Companion[Foo] {
      type C = Foo.type
      def apply() = Foo
    }
  }

  import Companion._

  val fc = companion[Foo]  // Type is Foo.type
  val s = fc.bar           // bar is accessible
}

This should be compiled with the -Ydependent-method-types flag if using Scala 2.9.x.

Coulter answered 7/2, 2012 at 8:0 Comment(6)
I added the actual Gist contents - the Gist might disappear and the link alone qualifies as a comment.Coulter
I'm getting "error: illegal dependent method type implicit def companion[T](implicit comp : Companion[T]) = comp.apply()" at line "implicit def companion[T](implicit comp : Companion[T]) = comp()" with scala 2.9.1. Am I doing it wrong? :-)Procedure
I'll add parenthetically that if the type Foo is visible then so is it's companion object, so although this looks quite nifty I don't think it's useful in practice.Krissy
That's a nice trick. I have to do some reading about dependent-method-types but this seem to work! ThanksProcedure
Any way to get this to work without that "per-companion boilerplate" part?Erasme
you could use a scala.meta macro to generate it, you would just have to tag the object with a known annotation.Collocate
C
9

You could use reflection to get the companion class and its instance, but that relies on Scala internals that might change in some far(?) future. And there is no type safety as you get an AnyRef. But there is no need to add any implicits to your classes and objects.

def companionOf[T : Manifest] : Option[AnyRef] = try{
  val classOfT = implicitly[Manifest[T]].erasure
  val companionClassName = classOfT.getName + "$"
  val companionClass = Class.forName(companionClassName)
  val moduleField = companionClass.getField("MODULE$")
  Some(moduleField.get(null))
} catch {
  case e => None
}

case class A(i : Int)

companionOf[A].collect{case a : A.type  => a(1)}
// res1: Option[A] = Some(A(1))
Condom answered 7/2, 2012 at 10:41 Comment(2)
Thanks, reflection would solve the problem... until scala version update, as companion object's name is not promised to be kept this way.Procedure
Well, thats true. But I dont think the naming would change any time soon, since this convention lasted at least since the early Scala 2.x days.Condom
R
6

I keep hitting this page when I forget how to do this and the answers are not hundred percent satisfactory for me. Here is how I do with reflection:

val thisClassCompanion = m.reflect(this).symbol.companion.asModule
val structural = m.reflectModule(thisClassCompanion)
                  .instance.asInstanceOf[{def doSth: Unit}]

You might need to verify that the class actually has a companion object or companion.asModule will throw a reflection exception is not a module

Updated: added another example for clarity:

    object CompanionUtil {

  import scala.reflect.runtime.{currentMirror => cm}

  def companionOf[T, CT](implicit tag: TypeTag[T]): CT = {
    Try[CT] {
      val companionModule = tag.tpe.typeSymbol.companion.asModule
      cm.reflectModule(companionModule).instance.asInstanceOf[CT]
    }
  }.getOrElse(throw new RuntimeException(s"Could not get companion object for type ${tag.tpe}"))

}
Riant answered 3/5, 2018 at 16:29 Comment(4)
What is m? Can you give a complete example? I am looking for something I can use like this: abstract class Whatever[T] = { val companion = companionOf[T] }Coranto
m is the current mirror, I get it just renaming an import import scala.reflect.runtime.{currentMirror => m}Riant
Note that CT is the type of the companion object which is T.type, e.g. companionOf[Foo, Foo.type] where Foo is the name of your class that has a companion object.Almita
Based on the answer, here's a method that gets the type reference of the companion object of the specified class object: <br/> def companionOf[CT](clazz: Class[_]): CT = { import scala.reflect.runtime.{currentMirror => cm} Try[CT] { val companionModule = cm.classSymbol(clazz).companion.asModule cm.reflectModule(companionModule).instance.asInstanceOf[CT] }.getOrElse(throw new RuntimeException(s"Could not get companion object for $clazz")) }Liliuokalani
L
1

Based on the answer by Miquel, here's a method that gets the type reference of the companion object of the specified class object:

  /**
    * Returns the companion object type reference for the specified class
    *
    * @param clazz The class whose companion is required
    * @tparam CT Type of the companion object
    * @return The type of the relevant companion object
    */
  def companionOf[CT](clazz: Class[_]): CT = {
    import scala.reflect.runtime.{currentMirror => cm}
    Try[CT] {
      val companionModule = cm.classSymbol(clazz).companion.asModule
      cm.reflectModule(companionModule).instance.asInstanceOf[CT]
    }.getOrElse(throw new RuntimeException(s"Could not get companion object for $clazz"))
  }
Liliuokalani answered 2/11, 2020 at 22:24 Comment(0)
C
0

Try macro-implemented type class HasCompanion by @Jasper-M https://gist.github.com/Jasper-M/1b6540fecfa3af9094dab1614038a1b5

import scala.language.experimental.macros
import scala.reflect.macros.whitebox

trait HasCompanion[A] {
  type Type
  def companion: Type
}
object HasCompanion {
  type Aux[A,C] = HasCompanion[A] { type Type = C }
  def apply[A](implicit hc: HasCompanion[A]): hc.type = hc
  implicit def mkHasCompanion[A,C]: Aux[A,C] = macro MacroHasCompanion.hasCompanionMacro[A]
}

object MacroHasCompanion {
  def hasCompanionMacro[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
    import c.universe._
    val aTpe = weakTypeOf[A]
    val companionTpe = aTpe.companion
    val companion = aTpe.typeSymbol.companion
    val HasCompanion = weakTypeOf[HasCompanion[A]]
    if (companionTpe =:= NoType)
      c.abort(c.enclosingPosition, s"No companion found for type $aTpe.")
    else if (!companion.isModule)
      c.abort(c.enclosingPosition, s"$aTpe is the companion.")
    else
      q"new $HasCompanion { type Type = $companionTpe; def companion = $companion }"
  }
}
def callSthStatic[T, C <: Worker](implicit hc: HasCompanion.Aux[T, C]) = {
  hc.companion.doSth
}

or

def callSthStatic[T](implicit hc: HasCompanion.Aux[T, _ <: Worker]) = {
  hc.companion.doSth
}

or

def callSthStatic[T, C](implicit hc: HasCompanion.Aux[T, C], ev: C <:< Worker) = {
  hc.companion.doSth
}

Similarly, a type class ToCompanion can be defined that is bi-directional on type level (from a case class to its companion object or from an object to its companion class)

Invoke construcotr based on passed parameter

import scala.language.experimental.macros
import scala.reflect.macros.whitebox

// type class
trait ToCompanion[A] {
  type Out
}

object ToCompanion {
  type Aux[A, Out0] = ToCompanion[A] {type Out = Out0}
  def apply[A](implicit tcc: ToCompanion[A]): ToCompanion.Aux[A, tcc.Out] = tcc

  implicit def mkToCompanion[A, B]: ToCompanion.Aux[A, B] = macro mkToCompanionImpl[A]

  def mkToCompanionImpl[A: c.WeakTypeTag](c: whitebox.Context): c.Tree = {
    import c.universe._
    val A = weakTypeOf[A]
    val companion = A.companion
    val ToCompanion = weakTypeOf[ToCompanion[A]]
    q"new $ToCompanion { type Out = $companion }"
  }
}
implicitly[ToCompanion.Aux[Base, Base.type]] // compiles
implicitly[ToCompanion.Aux[Base.type, Base]] // compiles

val tc = ToCompanion[Base.type]
implicitly[tc.Out =:= Base] // compiles
val tc1 = ToCompanion[Base]
implicitly[tc1.Out =:= Base.type] // compiles

// libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.10"
import shapeless.{Generic, HNil}

def callSthStatic[T] = new PartiallyApplied[T]

class PartiallyApplied[T] {
  def apply[C <: Worker]()(implicit
    toCompanion: ToCompanion.Aux[T, C],
    generic: Generic.Aux[C, HNil]
  ) = {
    generic.from(HNil).doSth
  }
}

callSthStatic[Base]()
Calceolaria answered 8/12, 2022 at 6:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.