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?

0

5 Answers 5

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.

6
  • I added the actual Gist contents - the Gist might disappear and the link alone qualifies as a comment. Feb 7, 2012 at 8:55
  • 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? :-) Feb 7, 2012 at 9:36
  • 3
    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. Feb 7, 2012 at 12:45
  • That's a nice trick. I have to do some reading about dependent-method-types but this seem to work! Thanks Feb 10, 2012 at 14:51
  • 7
    Any way to get this to work without that "per-companion boilerplate" part? Feb 5, 2014 at 17:08
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))
2
  • 1
    Thanks, reflection would solve the problem... until scala version update, as companion object's name is not promised to be kept this way. Feb 8, 2012 at 20:05
  • 1
    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.
    – MxFr
    Feb 9, 2012 at 13:38
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}"))

}
4
  • 1
    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] }
    – Max Murphy
    May 9, 2018 at 6:50
  • 1
    m is the current mirror, I get it just renaming an import import scala.reflect.runtime.{currentMirror => m}
    – Miquel
    May 9, 2018 at 18:12
  • 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.
    – Kevin
    Jun 9, 2020 at 13:04
  • 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")) } Nov 2, 2020 at 22:14
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"))
  }
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]()

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.