Creating a Singleton of a Class

As far as I understand do (companion) objects not define types, while classes do. Therefore with objects implicit ClassTags cannot be used.

Is there a common way to create singletons for classes?

objects define types. https://scastie.scala-lang.org/PXT1qWKdR9e3AxUDGcKU7Q :

import scala.reflect.ClassTag

object MyObject extends (String => Int) {
  def apply(arg: String): Int =
    arg.length
}

println(implicitly[ClassTag[MyObject.type]])

type MyType = MyObject.type
println(implicitly[ClassTag[MyType]])
1 Like

So only companion objects do not define types?

They do, actually – folks generally just don’t think of them that way, since they tend to contain only pure functions. They’re mostly used as namespaces, but they are types.

Basically, any object (created with that keyword) defines a class and the singleton instance of that class. They usually aren’t thought of as classes, but if you have an object named Foo, you can get at its type by saying Foo.type – it’s not terribly unusual to use that when you need to fill in a type parameter or suchlike.

2 Likes

Being a companion doesn’t change the type relation https://scastie.scala-lang.org/O4lKeuhsSjGdqzhLo4eKVg :

import scala.reflect.ClassTag

class MyType extends ((Int, Int) => Int) {
  def apply(a: Int, b: Int): Int =
    a + b
}

object MyType extends (String => Int) {
  def apply(arg: String): Int =
    arg.length
}

println(implicitly[ClassTag[MyType]])
println(implicitly[ClassTag[MyType.type]])

type MyClassType  = MyType
type MyObjectType = MyType.type
println(implicitly[ClassTag[MyClassType]])
println(implicitly[ClassTag[MyObjectType]])

val classInstance: MyType       = new MyType
val objectInstance: MyType.type = MyType

Notice that MyType means different things when used as a value and when used as a type. MyType when used as a value means instance of object MyType. MyType when used as a type (in type ascription) means class MyType. You need to understand that types and values are two different and separate namespaces and they have different syntax.

1 Like

I don’t understand why you say that objects define types, since this clearly does not work:

object Foo {}
val clazz = classOf[Foo]

And this does not work either:

object Foo {}
val clazz = classOf[Foo.type]

and

object Foo {}
val test: Foo = null

neither. It always fails with not found: type Foo

I believe, if I want to use single-tones that are defining a type, I need to do something like that:

class Single private()

object Single {
  val instance = new Single()
}

val single: Single = Single.instance

Or do I miss something?

Types are a compile-time concept. Foo.type is indeed a type, a full citizen of the universe of Scala types. Your val test: Foo = ... should be val test: Foo.type = ....

java.lang.Class is something else; that’s runtime reflection. See https://typelevel.org/blog/2017/02/13/more-types-than-classes.html

I don’t know why classOf[Foo.type] is forbidden by the compiler. The following references show that we aren’t the first ones to wonder that, but they don’t resolve the question, either:

But anyway, if you are sure you want to do reflection anyway, you can Foo.getClass (or Foo.getClass.asInstanceOf[Class[Foo.type]] to get rid of the type bound).

2 Likes

Foo.getClass.asInstanceOf[Class[Foo.type]] actually works but is not pleasant to look at, I have to say.

But what about my approach? It is not very Scala-like I suppose.

But what about my approach?

I don’t see an advantage?

1 Like

The spec disallows this, Standard Library | Scala 2.13 defines classOf only for class types, where class types are only those types that come from class or trait templates.

I can imagine that’s kind of useful for stability of the API if you want to be able to change implementation details of object. Class types have a JVM class where the is a clear 1:1 relationship between the type, the class declaration and the JVM class. Objects (I mean those things initiated with object, not instances of reference types) don’t have that. There is nothing (other than implementation details) that even guarantees that there is a single JVM class that implements the object.

for

object Foo {
  val someField = 7
  def someMethod = someField.toString()
}

scalac generates two class files, Foo$.scala and Foo.scala Shown from javap:

$ javap -p -c -s Foo.class

Compiled from "Foo.scala"
public final class Foo {
  public static java.lang.String someMethod();
    descriptor: ()Ljava/lang/String;
    Code:
       0: getstatic     #16                 // Field Foo$.MODULE$:LFoo$;
       3: invokevirtual #18                 // Method Foo$.someMethod:()Ljava/lang/String;
       6: areturn

  public static int someField();
    descriptor: ()I
    Code:
       0: getstatic     #16                 // Field Foo$.MODULE$:LFoo$;
       3: invokevirtual #22                 // Method Foo$.someField:()I
       6: ireturn

}

$ javap -p -c -s Foo$.class

Compiled from "Foo.scala"
public final class Foo$ {
  public static final Foo$ MODULE$;
    descriptor: LFoo$;

  private static final int someField;
    descriptor: I

  public static {};
    descriptor: ()V
    Code:
       0: new           #2                  // class Foo$
       3: dup
       4: invokespecial #14                 // Method "<init>":()V
       7: putstatic     #16                 // Field MODULE$:LFoo$;
      10: bipush        7
      12: putstatic     #18                 // Field someField:I
      15: return

  public int someField();
    descriptor: ()I
    Code:
       0: getstatic     #18                 // Field someField:I
       3: ireturn

  public java.lang.String someMethod();
    descriptor: ()Ljava/lang/String;
    Code:
       0: aload_0
       1: invokevirtual #24                 // Method someField:()I
       4: invokestatic  #30                 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
       7: invokevirtual #33                 // Method java/lang/Object.toString:()Ljava/lang/String;
      10: areturn

  private Foo$();
    descriptor: ()V
    Code:
       0: aload_0
       1: invokespecial #34                 // Method java/lang/Object."<init>":()V
       4: return
}

So you have an encoding where you have a JVM class Foo with static methods that delegate to JVM class Foo$ which contains a static field for someField, an instance accessor for someField that delegates to a singleton instance method.

If scala could accept classOf[Foo.type], I suppose it could return what in java would be Foo$.getclass, but that allows for a lot of mischief for questionable value. For example, you could create new instances of the singleton if you set the constructor to public, which may violate invariants in the language (which is worse than violating invariants in your application code).

If the object has a companion class, the jvm class implementing the class will also have the static forwarders to the companion. Maybe you can use that as a proxy?

You could also call out to java code, which will have no problem giving you a Foo$.getClass(). You could return that (and cast if needed).

This sorrow would all disappear if you tried not to rely on reflection, which is difficult to deal with if squinting doesn’t help anymore in calling class declarations and instances, JVM classes and types “roughly the same thing” or different perspectives on the same thing. You can’t get away with that in scala to the extent you can in Java.

Can we maybe help you find a solution to the problem you’re trying to solve with reflection in a way that is a better fit for scala?

3 Likes

Thank you for the exhaustive answer!

A brief description of what I try to do:

class Builder[A] {
  def build: A = ???
}

class Consumer {
  val element: SomeClass = new Builder[SomeClass].build
}

This example, of course, does not make much sense, but it should explain what I want to do.

Now, the type SomeClass cannot be an object because I cannot use objects with generics.

You can use the objects type Foo.type in generics. You can’t use Foo.type do that with classOf because classOf is special (probably/maybe at least in part for reasons indicated).

The following compiles.

object Singleton

class Builder[A] {
  def build: A = ???
}

class Consumer {
  val element: Singleton.type = new Builder[Singleton.type].build
}

For some limits with the reflection approach: Scastie - An interactive playground for Scala.

The “general” solution is to punt the “problem” to the caller:

class Builder[A](construct: () => A) {
  def build: A = construct()
}

though it’s questionable there is much more value in that than reducing the problem to

type Builder[A] = () => A

which is a different way to say – just use a () => A instead of a Builder[A]

At the end of the day, you can’t construct values for all types. Some, because you don’t have a sensible “default” to construct. Others, because they’re simply uninhabited. You can’t get a Builder[Nothing] that does something sensible, because Nothing has no values. I’m sure you can construct other types that can’t have any values too.

If it were possible to write a general implementation of Builder for all types, with only a type parameter as input would violate that.

1 Like

True, that works, thank you!

While using () => A is better than reflection based shenanigans, there is an uniform way to get both class and object Class instance https://scastie.scala-lang.org/hUUDjCfZRk2Y6ec3n7cMRw :

import scala.reflect.ClassTag

class MyType extends ((Int, Int) => Int) {
  def apply(a: Int, b: Int): Int =
    a + b
}

object MyType extends (String => Int) {
  def apply(arg: String): Int =
    arg.length
}

println(classOf[MyType] eq implicitly[ClassTag[MyType]].runtimeClass)
println(MyType.getClass eq implicitly[ClassTag[MyType.type]].runtimeClass)

The uniform way is implicitly[ClassTag[...]].runtimeClass, while classOf[...] is working only for classes and Singleton.getClass is working only for objects.

3 Likes